moonlight200 / minecraft-tmux-service

A systemd service that starts the minecraft server in a tmux session

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

minecraft.service launches script, then process exits

mstarks01 opened this issue · comments

Thanks for making these scripts available. They are by far the best of the minecraft init scripts I have run across.

I'm having a bit of an issue when launching it from systemctrl. The tmux and java process start, then stop a second or two later. The shell script works correctly; it's only when launching from systemd that I have an issue.

root@dorkcraft:/usr/local/bin/minecraft# systemctl status minecraft
* minecraft.service - Minecraft Server
     Loaded: loaded (/lib/systemd/system/minecraft.service; enabled; vendor preset: enabled)
     Active: failed (Result: exit-code) since Fri 2021-01-01 18:42:58 CST; 18min ago
    Process: 5246 ExecStart=/usr/local/bin/minecraft/service.sh start (code=exited, status=0/SUCCESS)
    Process: 5283 ExecStop=/usr/local/bin/minecraft/service.sh stop (code=exited, status=1/FAILURE)
   Main PID: 5250 (code=exited, status=0/SUCCESS)

Jan 01 18:42:52 dorkcraft systemd[1]: Starting Minecraft Server...
Jan 01 18:42:52 dorkcraft service.sh[5246]: Starting minecraft server in tmux session
Jan 01 18:42:52 dorkcraft systemd[1]: Started Minecraft Server.
Jan 01 18:42:58 dorkcraft service.sh[5283]: Server is not running!
Jan 01 18:42:58 dorkcraft systemd[1]: minecraft.service: Control process exited, code=exited, status=1/FAILURE
Jan 01 18:42:58 dorkcraft systemd[1]: minecraft.service: Failed with result 'exit-code'.

Here's the service script. The only changes I've made are to change the location to my install.

[Unit]
Description=Minecraft Server

Wants=network.target
After=network.target

[Service]
Type=forking
User=minecraft
Group=minecraft
KillMode=none

ProtectHome=read-only
ProtectSystem=full
PrivateDevices=no
NoNewPrivileges=yes
PrivateTmp=no
InaccessiblePaths=/root /sys /srv -/opt /media -/lost+found
ReadWritePaths=/usr/local/bin/minecraft
WorkingDirectory=/usr/local/bin/minecraft
ExecStart=/usr/local/bin/minecraft/service.sh start
ExecReload=/usr/local/bin/minecraft/service.sh reload
ExecStop=/usr/local/bin/minecraft/service.sh stop

[Install]
WantedBy=multi-user.target

And here's the shell script. It is executable. I changed the location here as well as a couple of minecraft arguments. Note that $MC_HOME/minecraft/minecraft-server is simply a link, so that is correct.

# Minecraft service that starts the minecraft server in a tmux session

MC_HOME="/usr/local/bin/minecraft"

TMUX_SOCKET="minecraft"
TMUX_SESSION="minecraft"

is_server_running() {
	tmux -L $TMUX_SOCKET has-session -t $TMUX_SESSION > /dev/null 2>&1
	return $?
}

mc_command() {
	cmd="$1"
	tmux -L $TMUX_SOCKET send-keys -t $TMUX_SESSION.0 "$cmd" ENTER
	return $?
}

start_server() {
	if is_server_running; then
		echo "Server already running"
		return 1
	fi
	echo "Starting minecraft server in tmux session"
	tmux -L $TMUX_SOCKET new-session -c $MC_HOME -s $TMUX_SESSION -d /usr/bin/java -Xms512M -Xmx1024M -jar $MC_HOME/minecraft/minecraft-server nogui
	return $?
}

stop_server() {
	if ! is_server_running; then
		echo "Server is not running!"
		return 1
	fi

	# Warn players
	echo "Warning players"
	mc_command "title @a times 3 14 3"
	for i in {10..1}; do
		mc_command "title @a subtitle {\"text\":\"in $i seconds\",\"color\":\"gray\"}"
		mc_command "title @a title {\"text\":\"Shutting down\",\"color\":\"dark_red\"}"
		sleep 1
	done

	# Issue shutdown
	echo "Kicking players"
	mc_command "kickall"
	echo "Stopping server"
	mc_command "stop"
	if [ $? -ne 0 ]; then
		echo "Failed to send stop command to server"
		return 1
	fi

	# Wait for server to stop
	wait=0
	while is_server_running; do
		sleep 1

		wait=$((wait+1))
		if [ $wait -gt 60 ]; then
			echo "Could not stop server, timeout"
			return 1
		fi
	done

	return 0
}

reload_server() {
	tmux -L $TMUX_SOCKET send-keys -t $TMUX_SESSION.0 "reload" ENTER
	return $?
}

attach_session() {
	if ! is_server_running; then
		echo "Cannot attach to server session, server not running"
		return 1
	fi

	tmux -L $TMUX_SOCKET attach-session -t $TMUX_SESSION
	return 0
}

case "$1" in
start)
	start_server
	exit $?
	;;
stop)
	stop_server
	exit $?
	;;
reload)
	reload_server
	exit $?
	;;
attach)
	attach_session
	exit $?
	;;
*)
	echo "Usage: ${0} {start|stop|reload|attach}"
	exit 2
	;;
esac

This is running in an Ubuntu 20.04 LXC container. More info:

root@dorkcraft:~# uname -a
Linux dorkcraft 5.4.34-1-pve #1 SMP PVE 5.4.34-2 (Thu, 07 May 2020 10:02:02 +0200) x86_64 x86_64 x86_64 GNU/Linux
root@dorkcraft:~# systemd --version
systemd 245 (245.4-4ubuntu3.3)
+PAM +AUDIT +SELINUX +IMA +APPARMOR +SMACK +SYSVINIT +UTMP +LIBCRYPTSETUP +GCRYPT +GNUTLS +ACL +XZ +LZ4 +SECCOMP +BLKID +ELFUTILS +KMOD +IDN2 -IDN +PCRE2 default-hierarchy=hybrid

+1 for all of the information provided.

I don't see any mistake in your changes. Does the Minecraft server log file state any reason why the server is shutting down?


Looking at the man page of systemd.exec, I found this in the section about ReadWritePaths=

Use ReadWritePaths= in order to whitelist specific paths for write access if ProtectSystem=strict is used.

It doesn't state what happens if ProtectSystem is set to full. Although unlikely, there is a chance that changing it to strict could help. A side effect would be, that /tmp gets protected, too. This needs to stay writeable for the minecraft user to allow tmux creating its socket in there.

ProtectSystem=strict
ReadWritePaths=/usr/local/bin/minecraft /tmp

On the other hand you could also try to relax the restrictions done by systemd since you're already using containerization. Although I have no experience with LXC and don't know what parts of the system are isolated by it.

If you want to leave it to LXC or don't need the extra protection, you can remove the lines from ProtectSystem to ReadWritePaths.

Thanks for replying. Upon further inspection, I realized that although the process didn't die when running from the script, I could not connect to the tmux session. It thought there were no sessions. So I started to simplify the command. I took out -c $MC_HOME, and when that didn't help, -L $TMUX_SOCKET. Taking out the socket allowed me to connect to the session. I suspect this might be due to an apparmor profile protecting the /tmp directory, although I also don't understand the use of -c $MC_HOME here, since it is only a directory.

I added the separate socket so that no one could accidentally close the tmux session, as it won't show up when using the default socket. This is to help preventing that the server stops without systemd knowing about it. If there was a pid file systemd could monitor the process directly, but I couldn't find one for tmux / the process running in the tmux terminal.
Using the default socket should work just as fine.

The -c $MC_HOME specifies the directory that the tmux session is started in. -c has a different meaning when used with tmux directly (similar to -L $TMUX_SOCKET) than when used with the new-session sub-comand. Basically this specifies the working directory and a cd $MC_HOME && java -jar ... has the same effect.

I found a way to create a pid file for the minecraft process. Maybe this will help with your problem, as systemd recommends having a PIDFile set if the service type is forking to determine the main process.
Determining the pid relies on the tmux session being the only one on the socket, so be careful with using the default socket.
I hope this helps 🙂

Thanks! I have made a couple of posts trying to get to the bottom of the socket issue in an unprivileged LXC container, but have yet to receive any feedback. I suspect that it's a container issue, but don't understand why the user should not be able to access their own socket.

I'll give your change a try when I have a few spare moments.

i went for a different approach, on my ProxMox LXC RockyLinux (now 8.6) container and wanted to share it :

Assumptions:

  • minecraft server is installed in /opt/minecraft
  • a minecraft user exists and has as home /opt/minecraft ( useradd -r -m -U -d /opt/minecraft -s /bin/bash minecraft )
  • the minecraft server jar-file is either named directly as server.jar, or is a symlink to the actual server-.jar ( i use symlink so easy switching/upgrading without having to fiddle with hardcoded filenames)

systemd unitfile:

[[Unit]
Description=Minecraft Server

[Service]
WorkingDirectory=/opt/minecraft
User=minecraft
Type=forking

ExecStart=/usr/bin/tmux new -s minecraft -d "/usr/bin/java -Xmx3096M -Xms2048M -XX:+UseG1GC -jar server.jar --nogui"
ExecReload=/usr/bin/tmux send-keys -t minecraft:0.0 'say SERVER RELOADING.' C-m 'reload' C-m
ExecStop=/usr/bin/tmux send-keys -t minecraft:0.0 'say SERVER SHUTTING DOWN. Saving map...' C-m 'save-all' C-m 'stop' C-m
ExecStop=/bin/sleep 2

[Install]
WantedBy=multi-user.target


Additional info:

  • as tmux is running under the specified user running tmux ls as root will not list the session, su into minecraft and then use tmux standard commands ( like ls, attatch, etc)