Last active
April 1, 2020 13:55
-
-
Save sbstp/9b4e3bfa36547da782cb428d61a062eb to your computer and use it in GitHub Desktop.
Wrapper script for the official minecraft server (or spigot) to shut it down gracefully via SIGTERM. Useful to put behind systemd and friends.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
from signal import signal, SIG_IGN, SIGTERM, SIGINT | |
import subprocess | |
import sys | |
USAGE = '''Usage: minecraftd [server jar path] [java options]*''' | |
JAVA_BIN = '/usr/bin/java' | |
JAVA_OPTS = ['-XX:+UseParNewGC', '-XX:+UseConcMarkSweepGC', | |
'-XX:+AggressiveOpts'] | |
def printf(fmt, *args): | |
sys.stdout.write(fmt % tuple(args)) | |
sys.stdout.flush() | |
if __name__ == '__main__': | |
if len(sys.argv) < 2: | |
print(USAGE) | |
exit(1) | |
jar_path = sys.argv[1] | |
opts = JAVA_OPTS + sys.argv[2:] | |
args = [JAVA_BIN, '-server'] + opts + ['-jar', jar_path, 'nogui'] | |
printf('minecraftd: %s\n', ' '.join(args)) | |
def preexec(): | |
signal(SIGTERM, SIG_IGN) | |
signal(SIGINT, SIG_IGN) | |
p = subprocess.Popen(args=args, stdin=subprocess.PIPE, | |
preexec_fn=preexec) | |
def handler(sig, _): | |
printf('minecraftd: stopping server\n') | |
p.stdin.write(b'stop\n') | |
p.stdin.flush() | |
signal(SIGTERM, handler) | |
signal(SIGINT, handler) | |
p.wait() | |
printf('minecraftd: exit code is %d', p.returncode) | |
sys.exit(p.returncode) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
from datetime import datetime | |
import os | |
import subprocess | |
import sys | |
WORLDS = ['survival', 'creative'] | |
PATH = '/home/minecraft/worlds' | |
KEEP_NUM = 168 # one week, every hour | |
def printf(fmt, *args): | |
sys.stdout.write(fmt % tuple(args)) | |
sys.stdout.flush() | |
def run(*args): | |
subprocess.run(args, stdout=subprocess.DEVNULL) | |
def btrfs_subvol_snapshot(src, dest): | |
run('btrfs', 'subvolume', 'snapshot', '-r', src, dest) | |
def btrfs_subvol_delete(path): | |
run('btrfs', 'subvolume', 'delete', path) | |
def world_versions(name): | |
return '%s.versions' % name | |
def create_snapshots(): | |
for world in WORLDS: | |
date = datetime.now().strftime('%Y-%m-%d_%H:%M:%S') | |
path = os.path.join(PATH, world) | |
versions_path = os.path.join(PATH, world_versions(world), date) | |
latest_path = os.path.join(PATH, world_versions(world), 'latest') | |
printf("versionsd: world %s, creating snapshot %s\n", world, | |
versions_path) | |
btrfs_subvol_snapshot(path, versions_path) | |
printf("versionsd: world %s, updating latest to %s\n", world, date) | |
btrfs_subvol_delete(latest_path) | |
btrfs_subvol_snapshot(versions_path, latest_path) | |
def rotate_snapshots(): | |
for world in WORLDS: | |
path = os.path.join(PATH, world_versions(world)) | |
versions = [snap for snap in os.listdir(path) if snap != 'latest'] | |
newest = list(sorted(versions, reverse=True)) | |
to_delete = newest[KEEP_NUM:] | |
for snap_path in to_delete: | |
snap_abs_path = os.path.join(path, snap_path) | |
printf("versionsd: world %s, deleting snapshot %s\n", world, | |
snap_abs_path) | |
btrfs_subvol_delete(os.path.join(path, snap_abs_path)) | |
if __name__ == '__main__': | |
create_snapshots() | |
rotate_snapshots() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
thanks, this helped me a lot! I used it to run a Minecraft server in a docker container (docker sends SIGTERM when a container is stopped)