Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
164bfc72db | ||
|
|
d2320a8ab4 | ||
|
|
85da15a26f | ||
|
|
3a3b4b8eb9 |
No files matched your search
@@ -1,5 +1,6 @@
|
||||
# Builds the .deb when a version tag is pushed and publishes it as a release,
|
||||
# with that version's CHANGELOG.md section as the release notes. To release,
|
||||
# Builds the .deb and the .rpm when a version tag is pushed and publishes them
|
||||
# as a release, with that version's CHANGELOG.md section as the release notes,
|
||||
# and to Gitea's apt and RPM repositories. To release,
|
||||
# set VERSION and CHANGELOG.md, commit, then:
|
||||
# git tag v$(cat VERSION) && git push origin v$(cat VERSION)
|
||||
name: release
|
||||
@@ -9,7 +10,7 @@ on:
|
||||
tags: ['v*']
|
||||
|
||||
jobs:
|
||||
deb:
|
||||
packages:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
@@ -27,10 +28,10 @@ jobs:
|
||||
run: |
|
||||
SUDO=$(command -v sudo || true)
|
||||
$SUDO apt-get update -qq
|
||||
$SUDO apt-get install -y -qq make python3 dpkg curl shellcheck python3-pyflakes
|
||||
$SUDO apt-get install -y -qq make python3 dpkg curl shellcheck python3-pyflakes rpm file
|
||||
|
||||
- name: Build (make check runs first)
|
||||
run: make deb
|
||||
run: make deb rpm
|
||||
|
||||
- name: Publish the release
|
||||
env:
|
||||
@@ -38,7 +39,7 @@ jobs:
|
||||
GITEA_REPO: ${{ gitea.repository }}
|
||||
# The job's own token; set a RELEASE_TOKEN secret instead if it isn't allowed to write releases
|
||||
GITEA_TOKEN: ${{ secrets.RELEASE_TOKEN || secrets.GITEA_TOKEN }}
|
||||
run: python3 packaging/gitea-release.py "$GITHUB_REF_NAME" build/*.deb
|
||||
run: python3 packaging/gitea-release.py "$GITHUB_REF_NAME" build/*.deb build/*.rpm
|
||||
|
||||
# Gitea's Debian registry, as an apt repository:
|
||||
# deb <server>/api/packages/<owner>/debian stable main
|
||||
@@ -64,3 +65,30 @@ jobs:
|
||||
echo "upload failed: HTTP $status"; cat response.txt; echo; exit 1
|
||||
fi
|
||||
echo "published $(basename "$deb") to $POOL"
|
||||
|
||||
# Gitea's RPM registry, as a dnf/yum repository:
|
||||
# <server>/api/packages/<owner>/rpm.repo
|
||||
- name: Publish to the RPM repository
|
||||
env:
|
||||
RPMREPO: ${{ gitea.server_url }}/api/packages/${{ gitea.repository_owner }}/rpm
|
||||
TOKEN: ${{ secrets.PACKAGE_TOKEN || secrets.GITEA_TOKEN }}
|
||||
run: |
|
||||
rpm=$(echo build/*.rpm)
|
||||
upload() {
|
||||
curl -sS -o response.txt -w '%{http_code}' --user "$GITHUB_ACTOR:$TOKEN" \
|
||||
--upload-file "$rpm" "$RPMREPO/upload"
|
||||
}
|
||||
status=$(upload)
|
||||
if [ "$status" = 409 ]; then
|
||||
# This version is there already (the tag was pushed again): replace it.
|
||||
# Gitea names the version either <version>-<release> or <version>.
|
||||
for v in "$(cat VERSION)-1" "$(cat VERSION)"; do
|
||||
curl -sS -f --user "$GITHUB_ACTOR:$TOKEN" -X DELETE \
|
||||
"$RPMREPO/package/terraria-server-manager/$v/noarch" && break
|
||||
done
|
||||
status=$(upload)
|
||||
fi
|
||||
if [ "$status" != 201 ]; then
|
||||
echo "upload failed: HTTP $status"; cat response.txt; echo; exit 1
|
||||
fi
|
||||
echo "published $(basename "$rpm") to $RPMREPO"
|
||||
@@ -0,0 +1,272 @@
|
||||
# AGENTS.md
|
||||
|
||||
Guidance for AI agents (and people) working on this repository.
|
||||
|
||||
## What this is
|
||||
|
||||
`terraria-server-manager` runs any number of vanilla Terraria and tModLoader
|
||||
dedicated servers on one Linux host, each as its own systemd service
|
||||
(`terraria@<name>.service`), managed with the `terractl` command. It ships as a
|
||||
Debian package and an RPM (Fedora, RHEL 9+), both built from the same
|
||||
`make install`. Server software is never bundled: official zips go in
|
||||
`/var/lib/terraria/dist` and are unpacked into each instance on demand.
|
||||
|
||||
The user-facing documentation is `docs/guide.md` (installed as the package's
|
||||
README). Read the sections relevant to a change before making it; the guide is
|
||||
the specification of how things behave.
|
||||
|
||||
## Layout
|
||||
|
||||
| Path | What it is |
|
||||
|---|---|
|
||||
| `bin/terractl` | The main tool, in bash. Everything on the local host. |
|
||||
| `lib/terractl-remote` | Python (standard library only, 3.9+): `--host`, `remote`, `daemon`, `copy`, `move`. Its daemon does everything by running `terractl` on its own host. |
|
||||
| `completions/bash/terractl` | Bash completion. |
|
||||
| `systemd/` | `terraria@.service`/`.socket` (one per instance), `terractld.service` (daemon for other hosts), `terraria-backup@.service`/`.timer` (scheduled backups). |
|
||||
| `config/` | `default/terractl` (user overrides, a conffile), `tmpfiles.d`, `sysusers.d`, `polkit` rule, `journald@terraria.conf` (a conffile). |
|
||||
| `packaging/deb/` | `control`, `conffiles`, maintainer scripts (`postinst`, `prerm`, `postrm`). |
|
||||
| `packaging/rpm/` | `terraria-server-manager.spec.in`: the RPM spec (`@VERSION@` filled in by `make rpm`). Plain scriptlets, no Fedora-only macros, so it builds anywhere `rpmbuild` runs. |
|
||||
| `packaging/gitea-release.py` | Publishes a release with that version's CHANGELOG section as notes. |
|
||||
| `.gitea/workflows/release.yaml` | On a `v*` tag: `make deb rpm` (on Ubuntu), publish the release and the apt and RPM repositories. |
|
||||
| `tools/windows/export-tmod-mods.ps1` | Helper for players to export their enabled tModLoader mods. |
|
||||
| `docs/guide.md`, `README.md`, `CHANGELOG.md`, `VERSION` | Docs and release metadata. |
|
||||
| `LICENSE` | MIT. Installed as `/usr/share/doc/terraria-server-manager/copyright` (and `%license` in the RPM). |
|
||||
|
||||
Placeholders `@VERSION@`, `@BINDIR@`, `@DOCDIR@` and `@PKGLIBDIR@` in
|
||||
`bin/terractl` and the unit files are filled in by `make install`/`make deb`.
|
||||
A copy run straight from the repo is a "development copy": it detects the
|
||||
unfilled placeholders and uses `lib/terractl-remote` from the source tree.
|
||||
|
||||
## Build and check
|
||||
|
||||
```bash
|
||||
make check # bash -n, sh -n on maintainer scripts, Python syntax, shellcheck, pyflakes
|
||||
make deb # runs check, builds build/terraria-server-manager_<version>_all.deb
|
||||
make rpm # runs check, builds build/terraria-server-manager-<version>-1.noarch.rpm
|
||||
```
|
||||
|
||||
`make rpm` needs `rpmbuild` (`apt install rpm` works; it runs with `--nodeps`
|
||||
because Debian has no RPM database). `make check` parses the spec with `rpmspec`
|
||||
when it's installed.
|
||||
|
||||
**`make check` must pass with shellcheck installed** (`apt install shellcheck
|
||||
python3-pyflakes`). The release workflow runs it, so a shellcheck warning blocks
|
||||
a release. Fix warnings rather than disabling them; a `# shellcheck disable=`
|
||||
needs a comment saying why (see the existing ones).
|
||||
|
||||
There is no automated test suite. See [Testing](#testing).
|
||||
|
||||
## How the system fits together
|
||||
|
||||
These invariants run through the whole code base. Keep them.
|
||||
|
||||
- **Servers run as the unprivileged `terraria` user**, sandboxed by
|
||||
`terraria@.service` (`ProtectSystem=strict`, `ProtectHome=yes`, writable only
|
||||
`ReadWritePaths=/var/lib/terraria/instances`, `UMask=0027`).
|
||||
- **Nothing in an instance may end up owned by root.** Commands run as root
|
||||
`chown` what they create (`fix_owner`, `chown -R` after `new`/`import`).
|
||||
`prepare` and `run` started as root switch to `terraria` with `runuser`
|
||||
(re-running `$SELF`, which is resolved before the script's `cd /`).
|
||||
- **Permission model:** root can do everything; members of the `terraria` group
|
||||
can read instances, start/stop/restart servers (polkit rule), use the console
|
||||
(`attach`, `cmd`, `watch`), read the servers' own journal (log namespace
|
||||
`terraria`), add zips to `dist/` and read backups. Group members can read but
|
||||
never change instance files. Every new command must fit this model and be
|
||||
added to the permissions table in the guide.
|
||||
- **`instance.conf` is sourced by bash.** Anything written into it (e.g. `ZIP=`)
|
||||
must be validated first (`safe_zip_name`). Instance names are validated by
|
||||
`valid_name` and can't start with `.`.
|
||||
- **One server per instance at a time:** the lock on `<instance>/.lock` (fd 9)
|
||||
is held for the server's lifetime. Commands that replace `server/` or delete
|
||||
things under a running server must check `instance_running`.
|
||||
- **`server/` is replaceable, `data/` is precious.** Unpacking happens in a temp
|
||||
folder and only replaces `server/` once complete; `server/.installed` records
|
||||
`<type> <zip>`. Nothing may delete or rewrite worlds without a backup.
|
||||
- **Changes that can lose data back up first** and leave things as they were on
|
||||
failure (see `cmd_upgrade`: validate everything, stop, back up, change, roll
|
||||
back the pin and restart on failure).
|
||||
- **Files outside `/var/lib/terraria`:** only the documented drop-ins in
|
||||
`/etc/systemd/system` (`terraria-backup@<name>.{timer,service}.d/terractl.conf`
|
||||
from `backup enable`, `<unit>.d/terractl-locations.conf` from `doctor --fix`).
|
||||
`postrm` purge removes them. Never overwrite admin files or unit files.
|
||||
- **The daemon (`terractld`) runs `terractl` as root** with `TERRACTL_VIA` set.
|
||||
Only commands listed in `EXEC_COMMANDS` in `lib/terractl-remote` can be run
|
||||
remotely. It runs with `ProtectSystem=full` and `ProtectHome=true`, plus write
|
||||
access to `/etc/systemd/system` only.
|
||||
- **Locations are configurable** (`TERRARIA_BASE`, `TERRARIA_DIST`,
|
||||
`TERRARIA_BACKUPS` in `/etc/default/terractl`), but unit files can't follow
|
||||
them; `location_warnings` and `doctor` detect mismatches. Never hard-code
|
||||
`/var/lib/terraria/...` in `bin/terractl`: use `$BASE_DIR`, `$DIST_DIR`,
|
||||
`$BACKUP_DIR`.
|
||||
|
||||
## Code conventions
|
||||
|
||||
### bash (`bin/terractl`)
|
||||
|
||||
- `set -euo pipefail`. Be careful with pipelines that can legitimately fail
|
||||
(`grep` with no match, `grep -q` closing a pipe early): capture to a variable
|
||||
first, or add `|| true`.
|
||||
- Errors: `die "message"` (exits 1); notes: `log "message"` (stderr). Both
|
||||
prefix `terractl:`. Normal output goes to stdout.
|
||||
- **Messages say what to do next.** When suggesting a command, use
|
||||
`hint root|group <command> [args]`, which adds `sudo` only when needed and
|
||||
shows `terractl --host <remote> ...` when run through the daemon.
|
||||
- Commands are `cmd_<name>` functions; sub-actions are `<command>_<action>`
|
||||
(e.g. `backup_enable`). Helpers are lower-case with underscores. Sections are
|
||||
separated by `# ---- name ----` banner comments.
|
||||
- Every non-obvious function gets a short comment above it saying what it does
|
||||
and why; match the existing density (comments explain intent and edge cases,
|
||||
not syntax).
|
||||
- Option parsing: a `while`/`case` loop, `die "unknown option '$1'"` for
|
||||
anything unexpected, and a `usage: terractl ...` message for wrong arguments.
|
||||
- Root checks: `[[ $EUID -eq 0 ]] || die "'<command>' needs root: sudo terractl ..."`.
|
||||
- `local` for all function variables. Globals are upper-case.
|
||||
- `EXTRACT_TMP` is the single temp-folder slot that `cleanup` (the EXIT trap)
|
||||
removes. Code that must survive a `die` in a helper runs it in a subshell
|
||||
with its own `trap cleanup EXIT` (see `make_backup`).
|
||||
|
||||
### Python (`lib/terractl-remote`)
|
||||
|
||||
- Standard library only, Python 3.9+. `make check` runs pyflakes on it.
|
||||
- It never touches instances directly: it runs `terractl` (locally, or via the
|
||||
daemon on the other host) so checks and ownership stay in one place.
|
||||
|
||||
### Shell in packaging
|
||||
|
||||
- Maintainer scripts and RPM scriptlets are POSIX `sh`, idempotent, and must
|
||||
never fail an install or removal because systemd isn't running
|
||||
(`[ -d /run/systemd/system ]` guards, `|| true`/`|| :` on best-effort steps).
|
||||
- **The two packages must behave the same.** A change to `packaging/deb/`
|
||||
scripts needs the matching change in the spec's `%post`/`%preun`/`%postun`,
|
||||
and the other way round. RPM scriptlets get `$1` as a count (`%post`: 1 =
|
||||
install, 2+ = upgrade; `%preun`/`%postun`: 0 = removal) where dpkg gets
|
||||
words. rpm has no purge: `%postun` on removal does a careful subset of the
|
||||
Debian purge (never deleting admin files or data).
|
||||
|
||||
### Portability
|
||||
|
||||
- Debian/Ubuntu and Fedora/RHEL are both supported. Don't hard-code `apt`:
|
||||
suggest installs with `pkg_install "<sudo prefix>" <what>` (maps package
|
||||
names per system), and mention both in the docs where commands differ.
|
||||
- Fedora and RHEL run firewalld by default; features that open a port should
|
||||
say how to allow it (`firewalld_open_cmd`), never open it automatically.
|
||||
- SELinux (enforcing on Fedora/RHEL) hasn't been looked at yet; note anything
|
||||
that might be affected (new executables, sockets, paths) in the PR.
|
||||
|
||||
## Adding or changing a command: checklist
|
||||
|
||||
1. `bin/terractl`: the `cmd_*` function, the `usage` text, and the dispatch at
|
||||
the bottom. Commands that don't take an instance name first (like `list`,
|
||||
`doctor`, `backup`, `fetch`) are dispatched in the first `case`, before
|
||||
`valid_name "$1"`.
|
||||
2. `lib/terractl-remote`: add it to `EXEC_COMMANDS` if it should work with
|
||||
`--host` (and a handler in `run_on_remote` if it moves files to or from the
|
||||
caller's computer, like `export`, `import` and `mods add`).
|
||||
3. `completions/bash/terractl`: the command list (`commands=`), and its
|
||||
arguments/options. Local-only commands go in the `else` branch's list.
|
||||
Instance names on a remote come from `_terractl_remote_instances`.
|
||||
4. `docs/guide.md`:
|
||||
- the section describing it (with examples using the `calamity` instance);
|
||||
- the **Permissions** table;
|
||||
- the **Tab completion** list, if completion changed;
|
||||
- the **Command reference**;
|
||||
- **Troubleshooting** and the `doctor` description, if relevant;
|
||||
- **Components** and **Directory layout** for new files or folders.
|
||||
5. `README.md` features list, for user-visible features.
|
||||
6. `CHANGELOG.md`: an entry in the newest section (see [Changelog](#changelog)).
|
||||
7. New installed files: `Makefile` `install` **and** `uninstall`, unit files
|
||||
through `$(SUBST)` if they contain placeholders, the RPM spec's `%files`
|
||||
(a file missing there fails `make rpm`), `packaging/deb/postrm` (purge) and
|
||||
the spec's `%postun` for anything created at runtime, and `conffiles` plus
|
||||
`%config(noreplace)` for files in `/etc` that users edit. New dependencies go
|
||||
in both `packaging/deb/control` and the spec (package names can differ).
|
||||
8. If it affects what can go wrong on a host, consider a check in `doctor`
|
||||
(`doc_problem warn|FAIL "<what>" "<fix command>" [<function that fixes it>]`;
|
||||
only pass a fix function for fixes that are safe to apply unattended).
|
||||
|
||||
## Testing
|
||||
|
||||
Agents usually can't run as root, use systemd, or install the package. The
|
||||
pattern used so far:
|
||||
|
||||
- Make a scratch folder (outside the repo) with `inst/`, `dist/`, `backups/`
|
||||
and point `TERRARIA_BASE`, `TERRARIA_DIST`, `TERRARIA_BACKUPS` at them, and
|
||||
`TERRARIA_USER` at your own user.
|
||||
- Fake root in a **copy** of the script:
|
||||
`sed -e 's/\$EUID/${FAKE_EUID:-$EUID}/g' bin/terractl > scratch/terractl`
|
||||
with `FAKE_EUID=0` exported, and a `runuser` stub on `PATH` that drops to the
|
||||
"service user" with `exec env -u FAKE_EUID "$@"` after the `--`. (Replacing
|
||||
`$EUID` with a literal `0` makes `prepare` re-run itself forever.)
|
||||
- Stub `systemctl` on `PATH` that keeps unit state in files (`is-active`,
|
||||
`is-enabled`, `is-failed`, `start`/`stop`/`enable`/`disable`, and `show -p`
|
||||
for the properties used). Point `SYSTEMD_ETC` at a scratch folder in the copy.
|
||||
- Build test zips with Python's `zipfile` (`zip` may not be installed): vanilla
|
||||
needs `<ver>/Linux/TerrariaServer.bin.x86_64`, tModLoader needs
|
||||
`start-tModLoaderServer.sh`.
|
||||
- `fetch` can be tested against the real sites; downloads are 40-60 MB.
|
||||
- Don't `pkill -f` a pattern that appears in your own command line.
|
||||
- Always run `make check`, and `make deb` when packaging changed. For unit
|
||||
files, run `systemd-analyze verify` on the **built** copies under
|
||||
`build/deb-root` (the sources contain placeholders).
|
||||
|
||||
Say clearly what was tested with stubs and what wasn't (real root, real
|
||||
systemd, real servers); the maintainer tests on a real host before releasing.
|
||||
|
||||
## Documentation style
|
||||
|
||||
- Plain, direct English; short sentences; British spelling where it differs.
|
||||
- Explain what happens and why, then show the command. Examples use the
|
||||
instance `calamity` and the remote `nas`.
|
||||
- Bold lead-in phrases for key points (`**Downgrades need --downgrade.**`).
|
||||
- Command reference and usage text are aligned two-column blocks; keep the
|
||||
columns aligned when adding lines.
|
||||
- Documented behaviour must match the code; update both in the same change.
|
||||
|
||||
## Changelog
|
||||
|
||||
`CHANGELOG.md` is for users, and each version's section is published as-is as
|
||||
its release notes on the Gitea release page (`gitea-release.py`). Write it so it
|
||||
reads well there.
|
||||
|
||||
- **Headings:** `## <version> - unreleased` while in progress,
|
||||
`## <version> - <YYYY-MM-DD>` once released. `gitea-release.py` takes the
|
||||
first word after `## ` as the version, so keep exactly that shape.
|
||||
- **Where entries go:** in the newest section if it's unreleased. If the newest
|
||||
section is already released (dated), start `## <next version> - unreleased`
|
||||
at the top **and** bump `VERSION` to match, in the same commit.
|
||||
- **Released sections are history:** don't edit them, except to fix a typo.
|
||||
- **Unreleased changes are edited, not appended to.** If something in the open
|
||||
section changes before release (renamed, moved, fixed), update its existing
|
||||
entry so the section describes the final state, rather than adding a
|
||||
"fixed X" line about something users never had.
|
||||
- **What an entry says:** the command and options as the user types them
|
||||
(`` `sudo terractl backup enable <name> [--every <when>]` ``), what it does
|
||||
and what they'll see, defaults, and anything they must do themselves
|
||||
(restart servers, run a command after upgrading, a new dependency). Full
|
||||
sentences; say "now" for changed behaviour and "before, ..." when the old
|
||||
behaviour could have caught people out.
|
||||
- **Order:** new features first, then changed behaviour, then fixes; the most
|
||||
important first within each. A large feature gets one bullet with
|
||||
sub-bullets for its parts (see `upgrade` and `fetch` in 0.0.12).
|
||||
- **Leave out** refactors, test-only changes, code style and doc rewording,
|
||||
unless the docs gained something users should know about (e.g. a new guide
|
||||
section on a setup that didn't work before).
|
||||
- Every user-visible change gets an entry in the same commit as the change.
|
||||
|
||||
## Releases
|
||||
|
||||
- `VERSION` holds the version (`0.0.N`) and must match the newest changelog
|
||||
section (see [Changelog](#changelog)).
|
||||
- To release: set the date, commit, then tag **`v<version>`** (annotated,
|
||||
message `terraria-server-manager <version>`). The workflow refuses a tag that
|
||||
doesn't match `VERSION`. Pushing the tag builds and publishes the release and
|
||||
the apt and RPM repositories, so only push when asked.
|
||||
|
||||
## Git
|
||||
|
||||
- Commit subjects are short, lower-case and imperative-ish
|
||||
(`add upgrade command; reinstall keeps the same version`), with a body
|
||||
listing the changes for anything non-trivial.
|
||||
- Everything is LF (`.gitattributes`); `build/`, `*.deb` and `__pycache__/` are
|
||||
ignored.
|
||||
- Commit, tag or push only when asked.
|
||||
@@ -1,5 +1,11 @@
|
||||
# Changelog
|
||||
|
||||
## 0.0.13 - 2026-10-02
|
||||
|
||||
- terraria-server-manager is now under the MIT license (`LICENSE`). Both packages include it: `/usr/share/doc/terraria-server-manager/copyright`, and on RPM systems also `/usr/share/licenses/terraria-server-manager/LICENSE`.
|
||||
- RPM package for Fedora, RHEL 9+ and compatible systems (AlmaLinux, Rocky Linux), built alongside the `.deb` and published to an RPM repository: add `https://gitea.adbraun.lol/api/packages/adam/rpm.repo` to `/etc/yum.repos.d/` and `sudo dnf install terraria-server-manager` (see the guide's "Installation"). It installs the same files and works the same way as the Debian package. `sudo dnf remove` stops all instances (saving their worlds) and turns off starting at boot, scheduled backups and the daemon; worlds, backups and settings are kept. Build it with `make rpm` (needs `rpmbuild`, also on Debian and Ubuntu).
|
||||
- `doctor` checks the firewall when firewalld is running (the default on Fedora and RHEL): it lists instance ports, and the daemon's, that firewalld doesn't allow, with the `firewall-cmd` command to open them. `new` shows that command too. Opening ports is never done automatically.
|
||||
- Messages that suggest installing something (`doctor`, `fetch`, and when `python3` or `openssl` is missing) now give the `dnf install` command on Fedora and RHEL instead of `apt install`, with the right package names (`libicu`, `polkit`).
|
||||
## 0.0.12 - 2026-10-02
|
||||
|
||||
- New `fetch` command to download server zips into `dist/`, named with their version:
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2026 Adam Brown
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -2,6 +2,7 @@
|
||||
#
|
||||
# make check syntax-check the scripts (and shellcheck, if installed)
|
||||
# make deb build build/terraria-server-manager_<version>_all.deb
|
||||
# make rpm build build/terraria-server-manager-<version>-1.noarch.rpm
|
||||
# sudo make install install directly (default PREFIX=/usr/local), no package
|
||||
# sudo make uninstall remove what 'make install' installed (keeps your data)
|
||||
# make clean remove build/
|
||||
@@ -33,8 +34,13 @@ SUBST = sed -e 's|@VERSION@|$(VERSION)|g' -e 's|@BINDIR@|$(BINDIR)|g' -e 's|@DOC
|
||||
MAINT_SCRIPTS := packaging/deb/postinst packaging/deb/prerm packaging/deb/postrm
|
||||
STAGE := build/deb-root
|
||||
DEB := build/$(NAME)_$(VERSION)_all.deb
|
||||
RPMTOP := build/rpm
|
||||
RPM := build/$(NAME)-$(VERSION)-1.noarch.rpm
|
||||
# What goes in the source tarball the RPM is built from
|
||||
RPM_SOURCES := Makefile VERSION LICENSE README.md CHANGELOG.md bin lib completions config docs \
|
||||
packaging systemd tools
|
||||
|
||||
.PHONY: all check install uninstall deb clean
|
||||
.PHONY: all check install uninstall deb rpm clean
|
||||
|
||||
all: check
|
||||
|
||||
@@ -48,6 +54,13 @@ check:
|
||||
else \
|
||||
echo "shellcheck not installed; skipped (apt install shellcheck)"; \
|
||||
fi
|
||||
@if command -v rpmspec >/dev/null; then \
|
||||
spec=$$(mktemp --suffix=.spec); \
|
||||
sed 's|@VERSION@|$(VERSION)|' packaging/rpm/$(NAME).spec.in > $$spec; \
|
||||
rpmspec -P $$spec >/dev/null; rc=$$?; rm -f $$spec; [ $$rc -eq 0 ]; \
|
||||
else \
|
||||
echo "rpmspec not installed; RPM spec not checked (apt install rpm, or dnf install rpm-build)"; \
|
||||
fi
|
||||
@if python3 -m pyflakes --version >/dev/null 2>&1; then \
|
||||
python3 -m pyflakes lib/terractl-remote; \
|
||||
else \
|
||||
@@ -73,6 +86,7 @@ install:
|
||||
install -m 644 config/tmpfiles.d/terractl.conf $(DESTDIR)$(TMPFILESDIR)/
|
||||
install -m 644 config/polkit/terractl.rules $(DESTDIR)$(POLKITDIR)/50-terractl.rules
|
||||
install -m 644 docs/guide.md $(DESTDIR)$(DOCDIR)/README.md
|
||||
install -m 644 LICENSE $(DESTDIR)$(DOCDIR)/copyright
|
||||
install -m 644 tools/windows/export-tmod-mods.ps1 $(DESTDIR)$(DATADIR)/
|
||||
install -m 644 completions/bash/terractl $(DESTDIR)$(COMPLETIONDIR)/terractl
|
||||
@# Never overwrite local settings
|
||||
@@ -128,5 +142,17 @@ deb: check
|
||||
find $(STAGE) -type d -exec chmod 755 {} +
|
||||
dpkg-deb --build --root-owner-group $(STAGE) $(DEB)
|
||||
|
||||
# Builds with plain rpmbuild (no Fedora-specific macros), so it works on Debian
|
||||
# and Ubuntu too, with: apt install rpm
|
||||
rpm: check
|
||||
rm -rf $(RPMTOP)
|
||||
install -d $(RPMTOP)/SOURCES $(RPMTOP)/SPECS
|
||||
tar -czf $(RPMTOP)/SOURCES/$(NAME)-$(VERSION).tar.gz --exclude=__pycache__ \
|
||||
--transform 's|^|$(NAME)-$(VERSION)/|' $(RPM_SOURCES)
|
||||
sed 's|@VERSION@|$(VERSION)|' packaging/rpm/$(NAME).spec.in > $(RPMTOP)/SPECS/$(NAME).spec
|
||||
@# --nodeps: on Debian and Ubuntu there is no RPM database to check BuildRequires against
|
||||
rpmbuild -bb --nodeps --define "_topdir $(CURDIR)/$(RPMTOP)" $(RPMTOP)/SPECS/$(NAME).spec
|
||||
cp $(RPMTOP)/RPMS/noarch/$(NAME)-$(VERSION)-1.noarch.rpm $(RPM)
|
||||
|
||||
clean:
|
||||
rm -rf build
|
||||
@@ -28,7 +28,7 @@ sudo terractl attach calamity
|
||||
|
||||
## Requirements
|
||||
|
||||
- Debian 12+ or Ubuntu 22.04+ (other systemd-based distributions should work with `make install`)
|
||||
- Debian 12+ or Ubuntu 22.04+ (`.deb`), or Fedora, RHEL 9+ and compatible (`.rpm`); other systemd-based distributions should work with `make install`
|
||||
- `unzip`, `systemd`; for tModLoader, also `curl` and ICU (`libicu`)
|
||||
- `python3` and `openssl`, for managing other hosts
|
||||
|
||||
@@ -46,10 +46,19 @@ sudo apt update
|
||||
sudo apt install terraria-server-manager
|
||||
```
|
||||
|
||||
Or download the `.deb` from the releases page, then:
|
||||
On Fedora, RHEL and compatible, from the RPM repository:
|
||||
|
||||
```bash
|
||||
sudo apt install ./terraria-server-manager_<version>_all.deb
|
||||
sudo curl -fsSL https://gitea.adbraun.lol/api/packages/adam/rpm.repo \
|
||||
-o /etc/yum.repos.d/terraria-server-manager.repo
|
||||
sudo dnf install terraria-server-manager
|
||||
```
|
||||
|
||||
Or download the `.deb` or `.rpm` from the releases page, then:
|
||||
|
||||
```bash
|
||||
sudo apt install ./terraria-server-manager_<version>_all.deb # Debian, Ubuntu
|
||||
sudo dnf install ./terraria-server-manager-<version>-1.noarch.rpm # Fedora, RHEL
|
||||
```
|
||||
|
||||
Or install from source, without a package:
|
||||
@@ -66,21 +75,24 @@ See [docs/guide.md](docs/guide.md). It covers installation, creating instances,
|
||||
|
||||
```
|
||||
bin/terractl The command-line tool
|
||||
systemd/ terraria@.service and terraria@.socket
|
||||
lib/terractl-remote Managing other hosts (--host, remote, daemon, copy, move), in Python
|
||||
systemd/ terraria@ service and socket, terractld, terraria-backup@ service and timer
|
||||
completions/bash/ Bash completion for terractl
|
||||
config/ /etc/default settings, sysusers.d and tmpfiles.d entries
|
||||
config/ /etc/default settings, sysusers.d, tmpfiles.d, polkit and journald files
|
||||
packaging/deb/ Debian package metadata and install/remove scripts
|
||||
packaging/rpm/ RPM spec (Fedora, RHEL)
|
||||
docs/guide.md Full documentation
|
||||
tools/windows/ export-tmod-mods.ps1 for exporting mods from Windows
|
||||
VERSION Current version, used by the build
|
||||
Makefile make check | deb | install | uninstall | clean
|
||||
Makefile make check | deb | rpm | install | uninstall | clean
|
||||
```
|
||||
|
||||
## Building
|
||||
|
||||
```bash
|
||||
make check # bash -n on all scripts, plus shellcheck if installed
|
||||
make check # syntax checks, plus shellcheck, pyflakes and rpmspec if installed
|
||||
make deb # build/terraria-server-manager_<version>_all.deb
|
||||
make rpm # build/terraria-server-manager-<version>-1.noarch.rpm (needs rpmbuild)
|
||||
```
|
||||
|
||||
To release, update `VERSION` and `CHANGELOG.md`, commit, and push a matching tag:
|
||||
@@ -89,7 +101,11 @@ To release, update `VERSION` and `CHANGELOG.md`, commit, and push a matching tag
|
||||
git tag v$(cat VERSION) && git push origin v$(cat VERSION)
|
||||
```
|
||||
|
||||
The `release` workflow (`.gitea/workflows/release.yaml`, Gitea Actions) then builds the `.deb`, publishes it as a release with that version's changelog section as the notes, and adds it to the apt repository. It needs a runner with the `ubuntu-latest` label. To build a package by hand instead, run `make deb`.
|
||||
The `release` workflow (`.gitea/workflows/release.yaml`, Gitea Actions) then builds the `.deb` and the `.rpm`, publishes them as a release with that version's changelog section as the notes, and adds them to the apt and RPM repositories. It needs a runner with the `ubuntu-latest` label. To build a package by hand instead, run `make deb` or `make rpm`.
|
||||
|
||||
## License
|
||||
|
||||
MIT; see [LICENSE](LICENSE).
|
||||
|
||||
## Disclaimer
|
||||
|
||||
|
||||
+51
-7
@@ -53,6 +53,30 @@ join_by() {
|
||||
}
|
||||
log() { echo "terractl: $*" >&2; }
|
||||
|
||||
# The command that installs <what> (unzip, curl, python3, openssl, icu, polkit)
|
||||
# with this system's package manager: pkg_install <sudo prefix> <what>
|
||||
pkg_install() {
|
||||
local sudo=$1 what=$2
|
||||
if command -v apt-get >/dev/null; then
|
||||
case $what in icu) what=libicu-dev ;; polkit) what=polkitd ;; esac
|
||||
echo "${sudo}apt install $what"
|
||||
elif command -v dnf >/dev/null; then
|
||||
case $what in icu) what=libicu ;; esac
|
||||
echo "${sudo}dnf install $what"
|
||||
else
|
||||
case $what in icu) what=ICU ;; esac
|
||||
echo "install $what with your package manager"
|
||||
fi
|
||||
}
|
||||
|
||||
# firewalld is on by default on Fedora and RHEL, and blocks new ports until
|
||||
# they're opened. Both need root (or polkit's permission) to ask.
|
||||
firewalld_running() { command -v firewall-cmd >/dev/null && firewall-cmd --state >/dev/null 2>&1; }
|
||||
firewalld_allows() { firewall-cmd --query-port="$1/tcp" >/dev/null 2>&1; }
|
||||
firewalld_open_cmd() { # firewalld_open_cmd <sudo prefix> <port>
|
||||
echo "${1}firewall-cmd --permanent --add-port=$2/tcp && ${1}firewall-cmd --reload"
|
||||
}
|
||||
|
||||
# Is <user> (default: whoever runs this) in the terraria group?
|
||||
in_group() {
|
||||
[[ " $(id -nG "${1:-$(id -un)}" 2>/dev/null) " == *" $SVC_USER "* ]]
|
||||
@@ -1054,6 +1078,10 @@ EOF
|
||||
$(hint root enable "$name" --now)
|
||||
$(hint group attach "$name")
|
||||
EOF
|
||||
if firewalld_running && ! firewalld_allows "$(inst_port "$name")"; then
|
||||
echo "firewalld is on; to let players connect, open its port:"
|
||||
echo " $(firewalld_open_cmd "$(if [[ $EUID -ne 0 ]]; then echo "sudo "; fi)" "$(inst_port "$name")")"
|
||||
fi
|
||||
location_warnings
|
||||
}
|
||||
|
||||
@@ -1839,16 +1867,16 @@ doctor_system() {
|
||||
|
||||
# Programs
|
||||
before=$(doc_problems_so_far)
|
||||
command -v unzip >/dev/null || doc_problem FAIL "unzip isn't installed, so servers can't be unpacked" "${DOC_SUDO}apt install unzip"
|
||||
command -v unzip >/dev/null || doc_problem FAIL "unzip isn't installed, so servers can't be unpacked" "$(pkg_install "$DOC_SUDO" unzip)"
|
||||
mapfile -t tml_confs < <(grep -l '^TYPE=tmodloader' "$BASE_DIR"/*/instance.conf 2>/dev/null || true)
|
||||
tml=${#tml_confs[@]}
|
||||
if [[ $tml -gt 0 ]]; then
|
||||
ldconfig=$(command -v ldconfig || echo /sbin/ldconfig)
|
||||
if ! "$ldconfig" -p 2>/dev/null | grep -q 'libicuuc\.so'; then
|
||||
doc_problem FAIL "ICU isn't installed; tModLoader servers stop with 'Aborted' without it" "${DOC_SUDO}apt install libicu-dev"
|
||||
doc_problem FAIL "ICU isn't installed; tModLoader servers stop with 'Aborted' without it" "$(pkg_install "$DOC_SUDO" icu)"
|
||||
fi
|
||||
command -v curl >/dev/null ||
|
||||
doc_problem FAIL "curl isn't installed; tModLoader servers need it to download .NET on their first start" "${DOC_SUDO}apt install curl"
|
||||
doc_problem FAIL "curl isn't installed; tModLoader servers need it to download .NET on their first start" "$(pkg_install "$DOC_SUDO" curl)"
|
||||
fi
|
||||
if [[ $(doc_problems_so_far) -eq $before ]]; then
|
||||
doc_ok "programs$(if [[ $tml -gt 0 ]]; then echo " (including ICU and curl for tModLoader)"; fi)"
|
||||
@@ -1938,12 +1966,17 @@ doctor_system() {
|
||||
|
||||
if [[ $(systemctl show -p LoadState --value polkit.service 2>/dev/null) != loaded ]]; then
|
||||
doc_problem warn "polkit isn't installed, so members of the '$SVC_USER' group need sudo to start and stop servers" \
|
||||
"${DOC_SUDO}apt install polkitd"
|
||||
"$(pkg_install "$DOC_SUDO" polkit)"
|
||||
fi
|
||||
|
||||
if systemctl is-enabled --quiet terractld.service 2>/dev/null; then
|
||||
if systemctl is-active --quiet terractld.service; then
|
||||
doc_ok "the daemon for other hosts (terractld) is running"
|
||||
d=${TERRACTL_LISTEN:-7776}; d=${d##*:}
|
||||
if [[ $EUID -eq 0 ]] && firewalld_running && ! firewalld_allows "$d"; then
|
||||
doc_problem warn "firewalld doesn't allow port $d/tcp, so other hosts can't reach the daemon" \
|
||||
"$(firewalld_open_cmd "$DOC_SUDO" "$d") (or allow it only from the hosts that manage this one)"
|
||||
fi
|
||||
else
|
||||
doc_problem FAIL "the daemon for other hosts (terractld) is enabled but not running" \
|
||||
"${DOC_SUDO}systemctl restart terractld; if it stops again: journalctl -u terractld" \
|
||||
@@ -2101,6 +2134,17 @@ doctor_instances() {
|
||||
doc_problem warn "$list use port $p, so only one of them can run at a time" \
|
||||
"change port= in the serverconfig.txt of all but one"
|
||||
done
|
||||
# Opening ports is left to a person: it's a decision about who can connect
|
||||
if [[ $EUID -eq 0 ]] && firewalld_running; then
|
||||
for p in "${!by_port[@]}"; do
|
||||
list=${by_port[$p]}
|
||||
if [[ -n $only && " $list " != *" $only "* ]]; then continue; fi
|
||||
if ! firewalld_allows "$p"; then
|
||||
doc_problem warn "firewalld doesn't allow port $p/tcp, so players can't connect to ${list// /, }" \
|
||||
"$(firewalld_open_cmd "$DOC_SUDO" "$p")"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
}
|
||||
|
||||
# Each instance is checked in a subshell (load_conf sets globals), so its
|
||||
@@ -2256,8 +2300,8 @@ cmd_fetch() {
|
||||
"") die "usage: terractl fetch vanilla|tmodloader [<version>|latest] [--upgrade <name>], or: terractl fetch vanilla|tmodloader --list" ;;
|
||||
*) die "unknown server type '$type' (vanilla or tmodloader)" ;;
|
||||
esac
|
||||
command -v curl >/dev/null || die "fetch needs curl: sudo apt install curl"
|
||||
command -v python3 >/dev/null || die "fetch needs python3: sudo apt install python3"
|
||||
command -v curl >/dev/null || die "fetch needs curl: $(pkg_install "sudo " curl)"
|
||||
command -v python3 >/dev/null || die "fetch needs python3: $(pkg_install "sudo " python3)"
|
||||
if [[ $list -eq 1 ]]; then
|
||||
[[ -z $version && -z $upgrade ]] || die "--list doesn't take a version or --upgrade"
|
||||
fetch_list "$type"
|
||||
@@ -2368,7 +2412,7 @@ run_remote_helper() {
|
||||
# A development copy uses the helper from its source tree
|
||||
if [[ $helper == *@* ]]; then helper="$(dirname "$self")/../lib/terractl-remote"; fi
|
||||
if [[ $ver == *@* ]]; then ver=development; fi
|
||||
command -v python3 >/dev/null || die "this needs python3 (sudo apt install python3)"
|
||||
command -v python3 >/dev/null || die "this needs python3: $(pkg_install "sudo " python3)"
|
||||
TERRACTL_BIN=$self TERRACTL_VERSION=$ver TERRACTL_CALLER_PWD=$CALLER_PWD \
|
||||
TERRACTL_LISTEN=${TERRACTL_LISTEN:-} exec python3 "$helper" "$@"
|
||||
}
|
||||
|
||||
+44
-10
@@ -102,7 +102,11 @@ The server can write only inside `instances/`. It reads `dist/` but can never ch
|
||||
|
||||
## Installation
|
||||
|
||||
The package works on Debian 12 or newer and Ubuntu 22.04 or newer. The easiest way is the apt repository, since `sudo apt upgrade` then keeps it up to date:
|
||||
There are packages for Debian 12 or newer and Ubuntu 22.04 or newer (`.deb`), and for Fedora, RHEL 9 or newer and compatible systems such as AlmaLinux and Rocky Linux (`.rpm`). Both install the same files and work the same way.
|
||||
|
||||
### Debian and Ubuntu
|
||||
|
||||
The easiest way is the apt repository, since `sudo apt upgrade` then keeps it up to date:
|
||||
|
||||
```bash
|
||||
sudo install -d -m 0755 /etc/apt/keyrings
|
||||
@@ -122,6 +126,28 @@ sudo apt install ./terraria-server-manager_0.0.9_all.deb
|
||||
|
||||
Using `apt install` (rather than `dpkg -i`) installs the dependencies automatically. `unzip` is required. `curl` and the ICU library are "recommended", which apt installs by default; they're only needed for tModLoader instances, which crash with `Aborted` without ICU. `polkitd` is also recommended: it lets members of the `terraria` group start and stop servers without `sudo` (see [Permissions](#permissions)).
|
||||
|
||||
### Fedora, RHEL and compatible
|
||||
|
||||
From the RPM repository, which `sudo dnf upgrade` then keeps up to date:
|
||||
|
||||
```bash
|
||||
sudo curl -fsSL https://gitea.adbraun.lol/api/packages/adam/rpm.repo \
|
||||
-o /etc/yum.repos.d/terraria-server-manager.repo
|
||||
sudo dnf install terraria-server-manager
|
||||
```
|
||||
|
||||
The repository is signed; the first time, dnf asks you to accept its key. Alternatively, download the `.rpm` from the releases page and install it directly:
|
||||
|
||||
```bash
|
||||
sudo dnf install ./terraria-server-manager-<version>-1.noarch.rpm
|
||||
```
|
||||
|
||||
dnf installs the "recommended" packages by default too: `curl` and `libicu` for tModLoader, and `polkit`. On RHEL and its rebuilds, ICU and curl are in the base repositories; nothing from EPEL is needed.
|
||||
|
||||
The RPM package is newer than the Debian one and has been tested less. With SELinux enforcing (the default on these systems), the servers run as an unconfined service; if SELinux blocks anything, `sudo ausearch -m avc -ts recent` shows what, and a report is welcome.
|
||||
|
||||
### What installing does
|
||||
|
||||
Installing the package:
|
||||
|
||||
- creates the `terraria` system user and group;
|
||||
@@ -130,10 +156,11 @@ Installing the package:
|
||||
|
||||
It doesn't start or enable anything, including the daemon for managing the host from elsewhere.
|
||||
|
||||
If you use a firewall, open each instance's port for **TCP**. Terraria uses TCP only.
|
||||
If you use a firewall, open each instance's port for **TCP**. Terraria uses TCP only. Fedora and RHEL have firewalld on by default, so there this is needed before anyone can connect; `terractl new` reminds you, and `terractl doctor` lists ports firewalld still blocks.
|
||||
|
||||
```bash
|
||||
sudo ufw allow 7777/tcp # example with ufw
|
||||
sudo firewall-cmd --permanent --add-port=7777/tcp && sudo firewall-cmd --reload # firewalld (Fedora, RHEL)
|
||||
sudo ufw allow 7777/tcp # ufw (if you use it on Ubuntu)
|
||||
```
|
||||
|
||||
### Upgrading the package
|
||||
@@ -141,9 +168,10 @@ sudo ufw allow 7777/tcp # example with ufw
|
||||
```bash
|
||||
sudo apt update && sudo apt upgrade # from the apt repository
|
||||
sudo apt install ./terraria-server-manager_<new-version>_all.deb # or from a downloaded .deb
|
||||
sudo dnf upgrade terraria-server-manager # Fedora, RHEL: from the RPM repository
|
||||
```
|
||||
|
||||
Running servers are not restarted. Each one picks up the new version the next time it starts. Your changes to `/etc/default/terractl` are kept; if the new package also changes that file, dpkg asks which version to keep.
|
||||
Running servers are not restarted. Each one picks up the new version the next time it starts. Your changes to `/etc/default/terractl` are kept. If the new package also changes that file, dpkg asks which version to keep; rpm keeps yours and saves the new one next to it as `/etc/default/terractl.rpmnew`.
|
||||
|
||||
### Removing the package
|
||||
|
||||
@@ -152,10 +180,12 @@ sudo apt remove terraria-server-manager # stops all instances (worlds are s
|
||||
sudo apt purge terraria-server-manager # also removes config and boot-time enablement
|
||||
```
|
||||
|
||||
Neither command deletes `/var/lib/terraria`, the servers' journal or the `terraria` user, so your worlds survive even a purge. To delete everything:
|
||||
On Fedora and RHEL there's only `sudo dnf remove terraria-server-manager`, since rpm has no separate purge. It sits between the two: it stops all instances (saving their worlds), turns off starting at boot, scheduled backups and the daemon, and removes the drop-ins terractl wrote itself (backup schedules, `doctor --fix`). It keeps drop-ins you added with `systemctl edit`, the daemon's keys in `/var/lib/terractl`, and a changed `/etc/default/terractl` (as `.rpmsave`).
|
||||
|
||||
None of these commands deletes `/var/lib/terraria`, the servers' journal or the `terraria` user, so your worlds survive. To delete everything:
|
||||
|
||||
```bash
|
||||
sudo rm -rf /var/lib/terraria && sudo userdel terraria
|
||||
sudo rm -rf /var/lib/terraria /var/lib/terractl && sudo userdel terraria
|
||||
```
|
||||
|
||||
### Upgrading from the `terraria-instance` package
|
||||
@@ -190,13 +220,16 @@ From the git repository:
|
||||
```bash
|
||||
make check # syntax-check the scripts (and shellcheck, if installed)
|
||||
make deb # builds build/terraria-server-manager_<version>_all.deb
|
||||
make rpm # builds build/terraria-server-manager-<version>-1.noarch.rpm
|
||||
sudo make install # or install directly without a package (PREFIX=/usr/local)
|
||||
sudo make uninstall # removes a 'make install' (keeps settings and data)
|
||||
```
|
||||
|
||||
`make install` with a prefix other than `/usr` puts the polkit rule in `/etc/polkit-1/rules.d/`, because polkit doesn't read `/usr/local`.
|
||||
|
||||
The version comes from the `VERSION` file. Set `Maintainer:` in `packaging/deb/control` to yourself before publishing packages.
|
||||
`make rpm` needs `rpmbuild`: `sudo dnf install rpm-build` on Fedora and RHEL, or `sudo apt install rpm` on Debian and Ubuntu (the spec uses no Fedora-only macros, so the package is the same wherever it's built).
|
||||
|
||||
The version comes from the `VERSION` file. Set `Maintainer:` in `packaging/deb/control` and `Packager:` in `packaging/rpm/terraria-server-manager.spec.in` to yourself before publishing packages.
|
||||
|
||||
### Changing locations
|
||||
|
||||
@@ -843,7 +876,7 @@ For each further computer, run `sudo terractl daemon token` on the host for a ne
|
||||
|
||||
### The daemon
|
||||
|
||||
`daemon enable` starts `terractld.service` and makes it start at boot. It listens on **TCP port 7776**, on all addresses. If a firewall is active, allow that port (for example `sudo ufw allow 7776/tcp`), ideally only from the computers that will manage the host. To listen on one address or on another port, set `TERRACTL_LISTEN` in `/etc/default/terractl` (e.g. `TERRACTL_LISTEN=10.0.10.77:7776`) and run `sudo systemctl restart terractld`.
|
||||
`daemon enable` starts `terractld.service` and makes it start at boot. It listens on **TCP port 7776**, on all addresses. If a firewall is active, allow that port (for example `sudo ufw allow 7776/tcp`, or with firewalld `sudo firewall-cmd --permanent --add-port=7776/tcp && sudo firewall-cmd --reload`), ideally only from the computers that will manage the host. To listen on one address or on another port, set `TERRACTL_LISTEN` in `/etc/default/terractl` (e.g. `TERRACTL_LISTEN=10.0.10.77:7776`) and run `sudo systemctl restart terractld`.
|
||||
|
||||
`sudo terractl daemon disable` turns it off again. `terractl daemon status` shows whether it's running, where it listens and its certificate's fingerprint, and with `sudo`, which computers it trusts. `sudo terractl daemon revoke <name>` takes a computer's access away.
|
||||
|
||||
@@ -1082,6 +1115,7 @@ terractl doctor --fix fixes 1 of them; the others need the steps shown.
|
||||
It checks:
|
||||
|
||||
- **The system:** the `terraria` user and group; `unzip`, and ICU and `curl` if there are tModLoader instances; the instances, zips and backups folders; unreadable zips; the systemd units, including old copies in `/etc/systemd/system` that replace the package's; whether the services may use the folders set in `/etc/default/terractl` (see [Changing locations](#changing-locations)); the servers' journal; polkit; the daemon, if it's on; and servers or backups that are still scheduled for instances that no longer exist.
|
||||
- **The firewall:** if firewalld is running, the instances' ports (and the daemon's) that it doesn't allow. Opening them is left to you, since it decides who can connect.
|
||||
- **Each instance:** files the server can't change, or that others can read; its server zip (missing, wrong type, or a `ZIP=` changed by hand that hasn't been applied yet); paths in `serverconfig.txt` that point somewhere else (e.g. after copying it by hand); enabled mods that aren't installed; instances sharing a port; and failed starts or backups.
|
||||
|
||||
`--fix` fixes what's safe to fix by itself, and says what it did: file ownership and modes (`fix-perms`), folder and zip permissions, the servers' journal access, the drop-in for moved folders, and boot links or backup schedules for instances that are gone. Everything else, such as deleting unit files or opening up home folders, is left for you, with the steps shown. `terractl doctor <name>` checks only that instance (and the system).
|
||||
@@ -1091,7 +1125,7 @@ Without `sudo`, `doctor` skips the checks that need root and says so. It exits w
|
||||
### Common problems
|
||||
|
||||
**tModLoader stops with `Aborted` right after "Launched Using Local Dotnet".**
|
||||
The ICU library is missing, so .NET aborts at startup. Fix it with `sudo apt install libicu-dev`, then restart the instance. To confirm, check that `ldconfig -p | grep -c libicu` prints more than 0.
|
||||
The ICU library is missing, so .NET aborts at startup. Fix it with `sudo apt install libicu-dev` (Fedora, RHEL: `sudo dnf install libicu`), then restart the instance. To confirm, check that `ldconfig -p | grep -c libicu` prints more than 0.
|
||||
|
||||
**`ls: cannot access '.../dotnet/shared/Microsoft.NETCore.App'` on the first start.**
|
||||
Harmless. tModLoader looks for old .NET versions to clean up before any have been installed.
|
||||
@@ -1166,7 +1200,7 @@ Its access was revoked with `daemon revoke`, or the host's `/var/lib/terractl` w
|
||||
- `.tar.gz` files for `export` and `import`;
|
||||
- `mods` actions. For `mods add` it completes `.tmod` files and folders; for `mods enable` and `mods remove`, the installed mods; for `mods disable`, the enabled ones.
|
||||
|
||||
It works with `sudo terractl` too. It needs the `bash-completion` package, which most Debian and Ubuntu systems already have (`sudo apt install bash-completion`). It takes effect in new terminal sessions.
|
||||
It works with `sudo terractl` too. It needs the `bash-completion` package, which most systems already have (`sudo apt install bash-completion`, or `sudo dnf install bash-completion`). It takes effect in new terminal sessions.
|
||||
|
||||
Completion runs as *you*, even when you type `sudo`. Instance and mod names therefore only complete if you can read `/var/lib/terraria`, which means being root or a member of the `terraria` group (see [Permissions](#permissions)). Otherwise, it falls back to the names of servers systemd currently knows about, and mod names don't complete.
|
||||
|
||||
|
||||
+2
-1
@@ -238,7 +238,8 @@ def ensure_cert():
|
||||
"-keyout", new_key, "-out", new_crt],
|
||||
stdout=subprocess.DEVNULL, stderr=subprocess.PIPE, text=True)
|
||||
except OSError:
|
||||
die("openssl is not installed (sudo apt install openssl)")
|
||||
manager = "dnf" if shutil.which("dnf") and not shutil.which("apt-get") else "apt"
|
||||
die(f"openssl is not installed (sudo {manager} install openssl)")
|
||||
finally:
|
||||
os.umask(old_umask)
|
||||
if proc.returncode != 0:
|
||||
|
||||
@@ -0,0 +1,126 @@
|
||||
# RPM package for Fedora, RHEL and similar. Built by 'make rpm', which fills in
|
||||
# @VERSION@ and makes the source tarball. The scriptlets use plain systemctl,
|
||||
# systemd-sysusers and systemd-tmpfiles rather than Fedora's macros, so the
|
||||
# package can be built anywhere rpmbuild runs (the release workflow builds it on
|
||||
# Ubuntu). They do what packaging/deb/ does for Debian; see the comments there.
|
||||
|
||||
# The license goes in /usr/share/licenses/<name>, as on Fedora, wherever it's built
|
||||
%global _docdir_fmt %{name}
|
||||
|
||||
Name: terraria-server-manager
|
||||
Version: @VERSION@
|
||||
Release: 1
|
||||
Summary: Run multiple Terraria and tModLoader dedicated servers with systemd
|
||||
License: MIT
|
||||
URL: https://gitea.adbraun.lol/adam/terraria-server-manager
|
||||
Packager: Adam Brown <adam@adbraun.lol>
|
||||
Source0: %{name}-%{version}.tar.gz
|
||||
BuildArch: noarch
|
||||
|
||||
BuildRequires: make
|
||||
Requires: bash
|
||||
Requires: coreutils
|
||||
Requires: util-linux
|
||||
Requires: unzip
|
||||
Requires: systemd
|
||||
Requires: python3
|
||||
Requires: openssl
|
||||
# Only needed for tModLoader instances (ICU: they stop with 'Aborted' without it)
|
||||
Recommends: curl
|
||||
Recommends: libicu
|
||||
# Lets the terraria group start, stop and restart servers without sudo
|
||||
Recommends: polkit
|
||||
Suggests: bash-completion
|
||||
|
||||
%description
|
||||
Provides the terraria@.service template and the terractl command for running
|
||||
any number of vanilla Terraria and tModLoader dedicated servers, each as its
|
||||
own systemd service under an unprivileged terraria user.
|
||||
|
||||
Server software is not included. 'terractl fetch' downloads the official server
|
||||
zips into /var/lib/terraria/dist, and they are unpacked into each instance on
|
||||
demand.
|
||||
|
||||
Hosts can also be managed from other hosts (terractl --host, copy, move)
|
||||
through the optional terractld daemon.
|
||||
|
||||
%prep
|
||||
%setup -q
|
||||
|
||||
%build
|
||||
# Nothing to build: shell and Python scripts
|
||||
|
||||
%install
|
||||
make install DESTDIR=%{buildroot} PREFIX=/usr SYSCONFDIR=/etc
|
||||
|
||||
%post
|
||||
systemd-sysusers terractl.conf >/dev/null 2>&1 || :
|
||||
systemd-tmpfiles --create terractl.conf >/dev/null 2>&1 || :
|
||||
if [ -d /run/systemd/system ]; then
|
||||
systemctl daemon-reload >/dev/null 2>&1 || :
|
||||
# On upgrades the daemon (if it's on) runs the new version right away. It
|
||||
# only serves other hosts; the game servers keep running.
|
||||
if [ "$1" -ge 2 ]; then
|
||||
systemctl try-restart terractld.service >/dev/null 2>&1 || :
|
||||
fi
|
||||
fi
|
||||
# Let the terraria group read the servers' journal (LogNamespace=terraria)
|
||||
/usr/bin/terractl journal-access >/dev/null 2>&1 || :
|
||||
|
||||
%preun
|
||||
# On removal (not upgrade), stop everything while terractl still exists, so each
|
||||
# server is sent "exit" and saves its world, and take back what was enabled:
|
||||
# rpm, unlike dpkg, has no separate purge.
|
||||
if [ "$1" -eq 0 ] && [ -d /run/systemd/system ]; then
|
||||
systemctl disable --now terractld.service >/dev/null 2>&1 || :
|
||||
for f in /etc/systemd/system/*.wants/terraria-backup@*.timer \
|
||||
/etc/systemd/system/*.wants/terraria@*.service; do
|
||||
[ -L "$f" ] && systemctl disable "${f##*/}" >/dev/null 2>&1 || :
|
||||
done
|
||||
systemctl stop 'terraria-backup@*.timer' 'terraria-backup@*.service' >/dev/null 2>&1 || :
|
||||
units=$(systemctl list-units --plain --no-legend --all 'terraria@*.service' | awk '{print $1}')
|
||||
if [ -n "$units" ]; then
|
||||
echo "Stopping Terraria instances: $units"
|
||||
# Unquoted on purpose: each unit name must be a separate argument
|
||||
# shellcheck disable=SC2086
|
||||
systemctl stop $units || :
|
||||
fi
|
||||
fi
|
||||
|
||||
%postun
|
||||
if [ "$1" -eq 0 ]; then
|
||||
# Drop-ins terractl wrote itself (backup schedules, 'doctor --fix'). Ones
|
||||
# you added with 'systemctl edit' are kept.
|
||||
rm -f /etc/systemd/system/terraria-backup@*.timer.d/terractl.conf \
|
||||
/etc/systemd/system/terraria-backup@*.service.d/terractl.conf \
|
||||
/etc/systemd/system/*.d/terractl-locations.conf
|
||||
rmdir /etc/systemd/system/terraria-backup@*.d /etc/systemd/system/terraria@.service.d \
|
||||
/etc/systemd/system/terractld.service.d 2>/dev/null || :
|
||||
cat >&2 <<MSG
|
||||
terraria-server-manager removed. Worlds, instances and backups in /var/lib/terraria,
|
||||
the daemon's keys in /var/lib/terractl and the 'terraria' user were kept. Remove
|
||||
them manually if they are no longer needed:
|
||||
sudo rm -rf /var/lib/terraria /var/lib/terractl && sudo userdel terraria
|
||||
MSG
|
||||
fi
|
||||
if [ -d /run/systemd/system ]; then
|
||||
systemctl daemon-reload >/dev/null 2>&1 || :
|
||||
fi
|
||||
|
||||
%files
|
||||
%license LICENSE
|
||||
/usr/bin/terractl
|
||||
/usr/lib/terraria-server-manager/
|
||||
/usr/lib/systemd/system/terraria@.service
|
||||
/usr/lib/systemd/system/terraria@.socket
|
||||
/usr/lib/systemd/system/terractld.service
|
||||
/usr/lib/systemd/system/terraria-backup@.service
|
||||
/usr/lib/systemd/system/terraria-backup@.timer
|
||||
/usr/lib/sysusers.d/terractl.conf
|
||||
/usr/lib/tmpfiles.d/terractl.conf
|
||||
/usr/share/polkit-1/rules.d/50-terractl.rules
|
||||
/usr/share/bash-completion/completions/terractl
|
||||
/usr/share/terraria-server-manager/
|
||||
%doc /usr/share/doc/terraria-server-manager/
|
||||
%config(noreplace) /etc/default/terractl
|
||||
%config(noreplace) /etc/systemd/journald@terraria.conf
|
||||
Reference in new issue
Block a user