From cb7503b6f395f08e317d39c000d181b5336d13d3 Mon Sep 17 00:00:00 2001 From: Dan Fuhry Date: Sun, 24 Apr 2022 20:18:58 -0400 Subject: [PATCH] Add systemd instantiated service file and per-session environment files Currently the management scripts allow for only a single instance of the minecraft server to be run, but it's actually not hard at all to support multiple instances. This commit makes the following changes: - Adds a new systemd service file, `minecraftd@.service`, which is an instantiated variant that overrides `SERVER_ROOT` and `SESSION_NAME` - Modifies `minecraftd.sh` to support sourcing an `environment` file from `SERVER_ROOT`, in addition to the existing system-wide file `/etc/conf.d/GAME`. - Instantiated versions of the minecraftd-backup service and timer. The one potentially unsafe assumption I'm making is that the `MAIN_EXECUTABLE` lives in the original compile-time `SERVER_ROOT`, which I know is the case for at least papermc and spigot. For installations where this is not the case, the environment file can override `SERVER_START_CMD`. The intended use case for this change is fairly common: using the same package to run multiple instances of a minecraft server on a single host. This would commonly be seen with, for example, multi-server bungeecord deployments. --- Makefile | 5 ++++- minecraftd-backup@.service.in | 15 +++++++++++++++ minecraftd-backup@.timer.in | 10 ++++++++++ minecraftd.sh.in | 6 +++++- minecraftd@.service.in | 20 ++++++++++++++++++++ 5 files changed, 54 insertions(+), 2 deletions(-) create mode 100644 minecraftd-backup@.service.in create mode 100644 minecraftd-backup@.timer.in create mode 100644 minecraftd@.service.in diff --git a/Makefile b/Makefile index 6fd9c22..c8cfa1b 100644 --- a/Makefile +++ b/Makefile @@ -10,7 +10,7 @@ datarootdir = $(prefix)/share mandir = $(prefix)/share/man man1dir = $(mandir)/man1 -SOURCES = minecraftd.sh.in minecraftd.conf.in minecraftd.service.in minecraftd.sysusers.in minecraftd.tmpfiles.in minecraftd-backup.service.in minecraftd-backup.timer.in +SOURCES = minecraftd.sh.in minecraftd.conf.in minecraftd.service.in minecraftd@.service.in minecraftd.sysusers.in minecraftd.tmpfiles.in minecraftd-backup.service.in minecraftd-backup@.service.in minecraftd-backup.timer.in minecraftd-backup@.timer.in OBJECTS = $(SOURCES:.in=) GAME = minecraft @@ -92,6 +92,9 @@ install: $(INSTALL_DATA) -D minecraftd.service "$(DESTDIR)$(libdir)/systemd/system/$(INAME).service" $(INSTALL_DATA) -D minecraftd-backup.service "$(DESTDIR)$(libdir)/systemd/system/$(INAME)-backup.service" $(INSTALL_DATA) -D minecraftd-backup.timer "$(DESTDIR)$(libdir)/systemd/system/$(INAME)-backup.timer" + $(INSTALL_DATA) -D minecraftd@.service "$(DESTDIR)$(libdir)/systemd/system/$(INAME)@.service" + $(INSTALL_DATA) -D minecraftd-backup@.service "$(DESTDIR)$(libdir)/systemd/system/$(INAME)-backup@.service" + $(INSTALL_DATA) -D minecraftd-backup@.timer "$(DESTDIR)$(libdir)/systemd/system/$(INAME)-backup@.timer" $(INSTALL_DATA) -D minecraftd.sysusers "$(DESTDIR)$(libdir)/sysusers.d/$(INAME).conf" $(INSTALL_DATA) -D minecraftd.tmpfiles "$(DESTDIR)$(libdir)/tmpfiles.d/$(INAME).conf" diff --git a/minecraftd-backup@.service.in b/minecraftd-backup@.service.in new file mode 100644 index 0000000..cd9c0ff --- /dev/null +++ b/minecraftd-backup@.service.in @@ -0,0 +1,15 @@ +[Unit] +Description=@GAME@ Server World Backup +After=local-fs.target + +[Service] +Type=oneshot +Environment=SERVER_ROOT=@SERVER_ROOT@/servers/%i +Environment=BACKUP_DEST=@SERVER_ROOT@/servers/%i/backup +Environment=SESSION_NAME=@GAME@-%i +ExecStart=/usr/bin/@INAME@ backup +User=@GAME_USER@ +Group=@GAME_USER@ + +[Install] +WantedBy=multi-user.target diff --git a/minecraftd-backup@.timer.in b/minecraftd-backup@.timer.in new file mode 100644 index 0000000..1ba98d0 --- /dev/null +++ b/minecraftd-backup@.timer.in @@ -0,0 +1,10 @@ +[Unit] +Description=Daily @GAME@ Server Backup + +[Timer] +OnCalendar=daily +AccuracySec=5min +Persistent=true + +[Install] +WantedBy=multi-user.target diff --git a/minecraftd.sh.in b/minecraftd.sh.in index 9be860a..ea7e491 100755 --- a/minecraftd.sh.in +++ b/minecraftd.sh.in @@ -33,7 +33,11 @@ declare -r GAME="@GAME@" [[ -n "${GAME_COMMAND_DUMP}" ]] && declare -r GAME_COMMAND_DUMP=${GAME_COMMAND_DUMP} || GAME_COMMAND_DUMP="@GAME_COMMAND_DUMP@" # Variables passed over the command line will always override the one from a config file -source /etc/conf.d/"${GAME}" 2>/dev/null || >&2 echo "Could not source /etc/conf.d/${GAME}" +for path in "/etc/conf.d/${GAME}" "${SERVER_ROOT}/environment"; do + if test -r "$path"; then + source "$path" || (echo "Could not source ${path}" >&2) + fi +done # Preserve the content of IDLE_SERVER without making it readonly [[ -n ${tmp_IDLE_SERVER} ]] && IDLE_SERVER=${tmp_IDLE_SERVER} diff --git a/minecraftd@.service.in b/minecraftd@.service.in new file mode 100644 index 0000000..68fc89a --- /dev/null +++ b/minecraftd@.service.in @@ -0,0 +1,20 @@ +[Unit] +Description=@GAME@ Server - %i instance +After=local-fs.target network.target multi-user.target + +[Service] +Type=forking +Environment=SERVER_ROOT=@SERVER_ROOT@/servers/%i +Environment=BACKUP_DEST=@SERVER_ROOT@/servers/%i/backup +Environment=SESSION_NAME=@GAME@-%i +Environment=MAIN_EXECUTABLE=../../@MAIN_EXECUTABLE@ +# create the "servers" directory followed by the instance directory +ExecStartPre=/bin/bash -c "/bin/test -d @SERVER_ROOT@/servers || /bin/install -d -m 0750 -o @GAME_USER@ -g @GAME_USER@ @SERVER_ROOT@/servers" +ExecStartPre=/bin/bash -c "/bin/test -d @SERVER_ROOT@/servers/%i || /bin/install -d -m 0750 -o @GAME_USER@ -g @GAME_USER@ @SERVER_ROOT@/servers/%i" +ExecStart=/usr/bin/@INAME@ start +ExecStop=/usr/bin/@INAME@ stop +User=@GAME_USER@ +Group=@GAME_USER@ + +[Install] +WantedBy=multi-user.target