Skip to content

Commit 3addc5c

Browse files
Fix non-graceful server shutdowns and improve signal handling (#41)
* fix: replace os.system() with subprocess.call() https://docs.python.org/3/library/subprocess.html#replacing-os-system os.system() ignores SIGINT, which is the stop signal set by the Dockerfile. This fix should immediately kill the Reforger process upon being asked to stop. * feat: send SIGTERM to reforger when stopping This provides a chance for the Reforger process to cleanup. * feat: kill Reforger process on unhandled exception If an unknown exception or a second interrupt occurs, ensure the process is killed. * feat: propagate exit code from Reforger process * feat: handle SIGTERM on launch.py This allows omitting STOPSIGNAL from Dockerfile. * fix: use shlex.join() when printing command This ensures the command can actually be invoked by the user in case some of the arguments contain whitespace or other unsafe characters. * fix: stop reforger using SIGINT instead of SIGTERM * refactor: explicitly skip kill clause when raising SystemExit The purpose of this is making the program flow more obvious, since proc.kill() already does nothing on completed processes.
1 parent 01eb9f4 commit 3addc5c

File tree

1 file changed

+38
-15
lines changed

1 file changed

+38
-15
lines changed

launch.py

Lines changed: 38 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,15 @@
22
import os
33
import random
44
import re
5+
import shlex
6+
import signal
57
import subprocess
8+
import sys
69
from pathlib import Path
710

11+
# On SIGTERM, raise KeyboardInterrupt instead of exiting abruptly.
12+
signal.signal(signal.SIGTERM, signal.default_int_handler)
13+
814
CONFIG_GENERATED = "/reforger/Configs/docker_generated.json"
915

1016

@@ -236,18 +242,35 @@ def bool_str(text):
236242

237243
config_path = CONFIG_GENERATED
238244

239-
launch = " ".join(
240-
[
241-
os.environ["ARMA_BINARY"],
242-
f"-config {config_path}",
243-
"-backendlog",
244-
"-nothrow",
245-
f"-maxFPS {os.environ['ARMA_MAX_FPS']}",
246-
f"-profile {os.environ['ARMA_PROFILE']}",
247-
f"-addonDownloadDir {os.environ['ARMA_WORKSHOP_DIR']}",
248-
f"-addonsDir {os.environ['ARMA_WORKSHOP_DIR']}",
249-
os.environ["ARMA_PARAMS"],
250-
]
251-
)
252-
print(launch, flush=True)
253-
os.system(launch)
245+
launch = [
246+
os.environ["ARMA_BINARY"],
247+
"-config",
248+
config_path,
249+
"-backendlog",
250+
"-nothrow",
251+
"-maxFPS",
252+
os.environ["ARMA_MAX_FPS"],
253+
"-profile",
254+
os.environ["ARMA_PROFILE"],
255+
"-addonDownloadDir",
256+
os.environ["ARMA_WORKSHOP_DIR"],
257+
"-addonsDir",
258+
os.environ["ARMA_WORKSHOP_DIR"],
259+
*shlex.split(os.environ["ARMA_PARAMS"]),
260+
]
261+
262+
print(shlex.join(launch), flush=True)
263+
264+
proc = subprocess.Popen(launch)
265+
266+
try:
267+
try:
268+
sys.exit(proc.wait())
269+
except KeyboardInterrupt:
270+
proc.send_signal(signal.SIGINT)
271+
sys.exit(proc.wait())
272+
except SystemExit:
273+
raise
274+
except BaseException:
275+
proc.kill()
276+
raise

0 commit comments

Comments
 (0)