From f0f2fe94ced7e0c4f1aa3a6643f260b87e7c2912 Mon Sep 17 00:00:00 2001 From: dgtlmoon Date: Tue, 2 Aug 2022 10:21:25 +0200 Subject: [PATCH] Handle SIGTERM for cleaner shutdowns (#737) --- changedetection.py | 30 +++++++++++++++++++++++++++- changedetectionio/__init__.py | 1 - changedetectionio/changedetection.py | 19 +++++++++++++++++- 3 files changed, 47 insertions(+), 3 deletions(-) diff --git a/changedetection.py b/changedetection.py index 9e76cc8c..f6e45040 100755 --- a/changedetection.py +++ b/changedetection.py @@ -6,6 +6,34 @@ # Read more https://github.com/dgtlmoon/changedetection.io/wiki from changedetectionio import changedetection +import multiprocessing +import signal +import os + +def sigterm_handler(_signo, _stack_frame): + import sys + print('Shutdown: Got SIGCHLD') + # https://stackoverflow.com/questions/40453496/python-multiprocessing-capturing-signals-to-restart-child-processes-or-shut-do + pid, status = os.waitpid(-1, os.WNOHANG | os.WUNTRACED | os.WCONTINUED) + + print('Sub-process: pid %d status %d' % (pid, status)) + if status != 0: + sys.exit(1) + + raise SystemExit if __name__ == '__main__': - changedetection.main() + signal.signal(signal.SIGCHLD, sigterm_handler) + # The only way I could find to get Flask to shutdown, is to wrap it and then rely on the subsystem issuing SIGTERM/SIGKILL + parse_process = multiprocessing.Process(target=changedetection.main) + parse_process.daemon = True + parse_process.start() + import time + + try: + while True: + time.sleep(1) + + except KeyboardInterrupt: + #parse_process.terminate() not needed, because this process will issue it to the sub-process anyway + print ("Exited - CTRL+C") diff --git a/changedetectionio/__init__.py b/changedetectionio/__init__.py index ed7f26e2..c2fe2b90 100644 --- a/changedetectionio/__init__.py +++ b/changedetectionio/__init__.py @@ -1259,7 +1259,6 @@ def notification_runner(): global notification_debug_log from datetime import datetime import json - while not app.config.exit.is_set(): try: # At the moment only one thread runs (single runner) diff --git a/changedetectionio/changedetection.py b/changedetectionio/changedetection.py index 2adf5ffc..6d07dfc8 100755 --- a/changedetectionio/changedetection.py +++ b/changedetectionio/changedetection.py @@ -4,6 +4,7 @@ import getopt import os +import signal import sys import eventlet @@ -11,7 +12,22 @@ import eventlet.wsgi from . import store, changedetection_app, content_fetcher from . import __version__ +# Only global so we can access it in the signal handler +datastore = None +app = None + +def sigterm_handler(_signo, _stack_frame): + global app + global datastore + + app.config.exit.set() + datastore.sync_to_json() + print('Shutdown: Got SIGTERM, DB saved to disk') + raise SystemExit + def main(): + global datastore + global app ssl_mode = False host = '' port = os.environ.get('PORT') or 5000 @@ -72,8 +88,10 @@ def main(): "Or use the -C parameter to create the directory.".format(app_config['datastore_path']), file=sys.stderr) sys.exit(2) + datastore = store.ChangeDetectionStore(datastore_path=app_config['datastore_path'], version_tag=__version__) app = changedetection_app(app_config, datastore) + signal.signal(signal.SIGTERM, sigterm_handler) # Go into cleanup mode if do_cleanup: @@ -111,4 +129,3 @@ def main(): else: eventlet.wsgi.server(eventlet.listen((host, int(port))), app) -