Skip to content

Instantly share code, notes, and snippets.

@sbstp
Last active April 1, 2020 13:55
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sbstp/9b4e3bfa36547da782cb428d61a062eb to your computer and use it in GitHub Desktop.
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.
#!/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)
#!/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()
@Derkades
Copy link

Derkades commented Apr 1, 2020

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)

@sbstp
Copy link
Author

sbstp commented Apr 1, 2020

There's a more polished version here

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment