server-wrapper hardening

This commit is contained in:
Eric Froemling 2021-03-02 11:22:02 -06:00
parent a09232daa1
commit 3245507578
No known key found for this signature in database
GPG Key ID: 89C93F0F8D6D5A98
2 changed files with 35 additions and 25 deletions

View File

@ -420,7 +420,7 @@
"assets/build/ba_data/audio/zoeOw.ogg": "https://files.ballistica.net/cache/ba1/a9/71/9286d55c45c37877f3267850f90b",
"assets/build/ba_data/audio/zoePickup01.ogg": "https://files.ballistica.net/cache/ba1/2f/09/36e691de67eb8f155449a7170861",
"assets/build/ba_data/audio/zoeScream01.ogg": "https://files.ballistica.net/cache/ba1/fd/a8/ad50785ce206e8dc3dcc7358b173",
"assets/build/ba_data/data/langdata.json": "https://files.ballistica.net/cache/ba1/29/af/04afc95d95a712c79207291083ba",
"assets/build/ba_data/data/langdata.json": "https://files.ballistica.net/cache/ba1/91/b8/a48083094a02608f5dff387d3469",
"assets/build/ba_data/data/languages/arabic.json": "https://files.ballistica.net/cache/ba1/ac/3e/c50dc4e98df47f858c3a73ac4272",
"assets/build/ba_data/data/languages/belarussian.json": "https://files.ballistica.net/cache/ba1/44/ed/5b972fa848cffb73723533c2ccb7",
"assets/build/ba_data/data/languages/chinese.json": "https://files.ballistica.net/cache/ba1/9d/63/d360eeff63bc64e098427498880d",
@ -444,7 +444,7 @@
"assets/build/ba_data/data/languages/polish.json": "https://files.ballistica.net/cache/ba1/cd/c1/82bf70c3ee4894791506f4da1a15",
"assets/build/ba_data/data/languages/portuguese.json": "https://files.ballistica.net/cache/ba1/98/45/ddeb7e797c02fb967e0c8b0dff7d",
"assets/build/ba_data/data/languages/romanian.json": "https://files.ballistica.net/cache/ba1/44/3c/7cc06ca8d5475e1687d0ed05bdbf",
"assets/build/ba_data/data/languages/russian.json": "https://files.ballistica.net/cache/ba1/ab/44/3d8b939f0c177cc46e8b0e4cae74",
"assets/build/ba_data/data/languages/russian.json": "https://files.ballistica.net/cache/ba1/15/e2/08ce8831b3a6ee2a5324a4112b04",
"assets/build/ba_data/data/languages/serbian.json": "https://files.ballistica.net/cache/ba1/e1/22/5471375791f8825a63e06371df29",
"assets/build/ba_data/data/languages/slovak.json": "https://files.ballistica.net/cache/ba1/b7/0a/fab820b96e7aa587ee56427ecdc2",
"assets/build/ba_data/data/languages/spanish.json": "https://files.ballistica.net/cache/ba1/72/6a/55e4f8ce819d0c39ee6d86e8c6a0",

View File

@ -89,6 +89,7 @@ class ServerManagerApp:
self._subprocess_sent_clean_exit = False
self._subprocess_sent_unclean_exit = False
self._subprocess_thread: Optional[Thread] = None
self._subprocess_exited_cleanly: bool = False
# This may override the above defaults.
self._parse_command_line_args()
@ -584,23 +585,42 @@ class ServerManagerApp:
if os.name == 'nt' else './ballisticacore_headless')
assert self._ba_root_path is not None
self._subprocess = None
# Launch!
try:
self._subprocess = subprocess.Popen(
[binary_name, '-cfgdir', self._ba_root_path],
stdin=subprocess.PIPE,
cwd='dist')
except Exception as exc:
print(f'Error launching server subprocess: {exc}',
print(
f'{Clr.RED}Error launching server subprocess: {exc}{Clr.RST}',
file=sys.stderr,
flush=True)
# Do the thing.
try:
self._run_subprocess_until_exit()
except Exception as exc:
print(f'{Clr.RED}Error running server subprocess: {exc}{Clr.RST}',
file=sys.stderr,
flush=True)
# Do the thing.
# No matter how this ends up, make sure the process is dead after.
if self._subprocess is not None:
try:
self._run_subprocess_until_exit()
finally:
self._kill_subprocess()
self._kill_subprocess()
# Avoid super fast death loops.
if (not self._subprocess_exited_cleanly and self._auto_restart
and not self._done):
time.sleep(5.0)
# If they don't want auto-restart, we'll exit the whole wrapper.
# (and with an error code if things ended badly).
if not self._auto_restart:
self._wrapper_shutdown_desired = True
if not self._subprocess_exited_cleanly:
self._should_report_subprocess_error = True
self._reset_subprocess_vars()
# If we want to die completely after this subprocess has ended,
# tell the main thread to die.
@ -662,8 +682,10 @@ class ServerManagerApp:
self._subprocess.stdin.flush()
def _run_subprocess_until_exit(self) -> None:
if self._subprocess is None:
return
assert current_thread() is self._subprocess_thread
assert self._subprocess is not None
assert self._subprocess.stdin is not None
# Send the initial server config which should kick things off.
@ -706,25 +728,13 @@ class ServerManagerApp:
code: Optional[int] = self._subprocess.poll()
if code is not None:
# If they don't want auto-restart, exit the whole wrapper.
# (and make sure to exit with an error code if things ended
# badly here).
if not self._auto_restart:
self._wrapper_shutdown_desired = True
if code != 0:
self._should_report_subprocess_error = True
clr = Clr.CYN if code == 0 else Clr.RED
print(
f'{clr}Server subprocess exited'
f' with code {code}.{Clr.RST}',
file=sys.stderr,
flush=True)
self._reset_subprocess_vars()
# Avoid super fast death loops.
if code != 0 and self._auto_restart:
time.sleep(5.0)
self._subprocess_exited_cleanly = (code == 0)
break
time.sleep(0.25)
@ -809,6 +819,7 @@ class ServerManagerApp:
self._subprocess_sent_clean_exit = False
self._subprocess_sent_unclean_exit = False
self._subprocess_force_kill_time = None
self._subprocess_exited_cleanly = False
def _kill_subprocess(self) -> None:
"""End the server subprocess if it still exists."""
@ -827,7 +838,6 @@ class ServerManagerApp:
self._subprocess.wait(timeout=10)
except subprocess.TimeoutExpired:
self._subprocess.kill()
self._reset_subprocess_vars()
print(f'{Clr.CYN}Subprocess stopped.{Clr.RST}',
file=sys.stderr,
flush=True)