Fix non-graceful server shutdowns and improve signal handling#41
Merged
BrettMayson merged 9 commits intoacemod:mainfrom Aug 29, 2025
Merged
Fix non-graceful server shutdowns and improve signal handling#41BrettMayson merged 9 commits intoacemod:mainfrom
BrettMayson merged 9 commits intoacemod:mainfrom
Conversation
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.
This provides a chance for the Reforger process to cleanup.
If an unknown exception or a second interrupt occurs, ensure the process is killed.
This allows omitting STOPSIGNAL from Dockerfile.
This ensures the command can actually be invoked by the user in case some of the arguments contain whitespace or other unsafe characters.
The purpose of this is making the program flow more obvious, since proc.kill() already does nothing on completed processes.
|
this seems like a valid change, is there a reason why this hasn't been merged? |
BrettMayson
approved these changes
Aug 29, 2025
This file contains hidden or 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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This fixes the server not gracefully shutting down when using
docker compose down/docker compose restartdue to SIGINT being ignored byos.system().The script will gracefully shutdown with with both SIGINT and SIGTERM. This PR does not modify the Dockerfile, so SIGINT remains the default stop signal.
When a SIGINT or SIGTERM is first received, the script will send SIGINT to the Reforger process and wait for it to exit. The subprocess will be killed if a second interrupt is sent, although in practice this won't matter since SIGKILL is sent when Docker times out.
The script will also pass-through the Reforger process's exit code unless it is killed by the script, in which case it will exit with code 1.
As a side effect of passing an argument list to
subprocess.Popen, command injection is no longer possible through the environment variablesARMA_BINARY,ARMA_MAX_FPS,ARMA_PROFILE, orARMA_WORKSHOP_DIR. TheARMA_PARAMSvariable can still be used to pass custom arguments.shlex.join()is now used when printing the server command, which may result in slightly different outputs.Context
I had set up a server using this project's image and was planning to run
docker compose restartperiodically via cron jobs. However, when I tested the command, the logs would not show any indication of it shutting down and it would consistently exceed Docker's default timeout of 10 seconds before being killed. I increased this to 120s with the same results.Bacon informed me that the script may not be passing down SIGINT to Reforger, so I looked in
launch.pyfor clues and foundos.system(). It's documentation didn't mention anything about SIGINT, but I coincidentally came across this in the subprocess docs:I looked at the Dockerfile and saw it used
STOPSIGNAL SIGINT, which is how I realized what was causing the issue. I later verified this behaviour with these test scripts, albeit on Python 3.12 and not 3.9 as used by Debian Bullseye.I've updated my server to use the following image:
From preliminary testing,
docker compose restartnow gracefully shuts down the server, although it had gotten close to the 10s timeout.P.S. I could not get ghcr.yaml's test job to work on my fork as it kept hanging on
steam-query.pywithout any output.