From 4f8ebddcbdd707af8e67e0651be9e325c7f130ce Mon Sep 17 00:00:00 2001 From: Jeremy Cline Date: Apr 18 2025 16:42:39 +0000 Subject: Configure an alarm to kill requests that hang Register an alarm signal handler for the request handler child process and configure an alarm for an hour per request. The alarm handler exits the child process, which will cause a warning-level log before continuing to handle requests. Signed-off-by: Jeremy Cline --- diff --git a/src/server.py b/src/server.py index c7f51b7..2207e59 100644 --- a/src/server.py +++ b/src/server.py @@ -74,6 +74,8 @@ else: MAX_FAST_RECONNECTIONS = 5 FAST_RECONNECTION_SECONDS = 5 SLOW_RECONNECTION_SECONDS = 60 +# How long to wait for the request handler before killing it +CHILD_TIMEOUT_SECS = 60 * 60 # Infrastructure @@ -2695,6 +2697,7 @@ request_handlers[None] = RequestHandler(unknown_request_handler, _CHILD_OK = 0 # Handled a request _CHILD_CONNECTION_REFUSED = 1 # Connection to the bridge was refused _CHILD_BUG = 2 # A bug in the child +_CHILD_TIMEOUT = 3 # Request handler timed out and was killed # Undefined values are treated as _CHILD_BUG: @@ -2758,6 +2761,10 @@ def request_handling_child(config): return _CHILD_OK +def sigalarm_handler(*args): + sys.exit(_CHILD_TIMEOUT) + + def main(): # Any blocking socket operations time out after an hour socket.setdefaulttimeout(60 * 60) @@ -2785,6 +2792,9 @@ def main(): child_pid = os.fork() if child_pid == 0: try: + # Kill the child process if it fails to finish in an hour + signal.signal(signal.SIGALRM, sigalarm_handler) + signal.alarm(CHILD_TIMEOUT_SECS) status = request_handling_child(config) logging.shutdown() os._exit(status) @@ -2805,6 +2815,8 @@ def main(): else: time.sleep(SLOW_RECONNECTION_SECONDS) fast_reconnections_done = 0 + elif os.WIFEXITED(status) and os.WEXITSTATUS(status) == _CHILD_TIMEOUT: + logging.warning("Child timed out handling request and was killed") else: # _CHILD_BUG, unknown status code or WIFSIGNALED logging.error('Child died with status %d', status) break