From a83547257deedf0d79817c74988e3ed9b4e3c0e7 Mon Sep 17 00:00:00 2001 From: Alexander Kozlovsky Date: Wed, 24 Aug 2022 18:22:15 +0200 Subject: [PATCH] Return error exit code from the Core process if the Core session fails --- src/tribler/core/components/base.py | 2 ++ src/tribler/core/start_core.py | 23 +++++++++++++++++++---- src/tribler/gui/core_manager.py | 13 ++++++++----- 3 files changed, 29 insertions(+), 9 deletions(-) diff --git a/src/tribler/core/components/base.py b/src/tribler/core/components/base.py index 5c53fff0001..1e0a13fdc0b 100644 --- a/src/tribler/core/components/base.py +++ b/src/tribler/core/components/base.py @@ -86,6 +86,7 @@ def __init__(self, config: TriblerConfig = None, components: List[Component] = ( shutdown_event: Event = None, notifier: Notifier = None, failfast: bool = True): # deepcode ignore unguarded~next~call: not necessary to catch StopIteration on infinite iterator self.id = next(Session._next_session_id) + self.exit_code = 0 self.failfast = failfast self.logger = logging.getLogger(self.__class__.__name__) self.config: TriblerConfig = config or TriblerConfig() @@ -164,6 +165,7 @@ def _reraise_startup_exception_in_separate_task(self): async def exception_reraiser(): e = self._startup_exception if isinstance(e, ComponentStartupException) and e.component.tribler_should_stop_on_component_error: + self.exit_code = 1 self.shutdown_event.set() # the exception should be intercepted by event loop exception handler diff --git a/src/tribler/core/start_core.py b/src/tribler/core/start_core.py index c91d67975ad..c22ec0ce79c 100644 --- a/src/tribler/core/start_core.py +++ b/src/tribler/core/start_core.py @@ -4,6 +4,7 @@ import os import signal import sys +from pathlib import Path from typing import List from tribler.core import notifications @@ -92,7 +93,12 @@ def components_gen(config: TriblerConfig): yield GigachannelManagerComponent() -async def core_session(config: TriblerConfig, components: List[Component]): +async def core_session(config: TriblerConfig, components: List[Component]) -> int: + """ + Async task for running a new Tribler session. + + Returns an exit code, which is non-zero if the Tribler session finished with an error. + """ session = Session(config, components, failfast=False) signal.signal(signal.SIGTERM, lambda signum, stack: session.shutdown_event.set) async with session.start() as session: @@ -108,12 +114,16 @@ async def core_session(config: TriblerConfig, components: List[Component]): session.notifier[notifications.tribler_shutdown_state]("Saving configuration...") config.write() + return session.exit_code + -def run_tribler_core_session(api_port, api_key, state_dir, gui_test_mode=False): +def run_tribler_core_session(api_port: str, api_key: str, state_dir: Path, gui_test_mode: bool = False) -> int: """ This method will start a new Tribler session. Note that there is no direct communication between the GUI process and the core: all communication is performed through the HTTP API. + + Returns an exit code value, which is non-zero if the Tribler session finished with an error. """ logger.info(f'Start tribler core. API port: "{api_port}". ' f'API key: "{api_key}". State dir: "{state_dir}". ' @@ -154,7 +164,7 @@ def run_tribler_core_session(api_port, api_key, state_dir, gui_test_mode=False): loop.set_exception_handler(exception_handler.unhandled_error_observer) try: - loop.run_until_complete(core_session(config, components=list(components_gen(config)))) + exit_code = loop.run_until_complete(core_session(config, components=list(components_gen(config)))) finally: if trace_logger: trace_logger.close() @@ -163,6 +173,8 @@ def run_tribler_core_session(api_port, api_key, state_dir, gui_test_mode=False): for handler in logging.getLogger().handlers: handler.flush() + return exit_code + def run_core(api_port, api_key, root_state_dir, parsed_args): logger.info('Running Core' + ' in gui_test_mode' if parsed_args.gui_test_mode else '') @@ -171,4 +183,7 @@ def run_core(api_port, api_key, root_state_dir, parsed_args): with single_tribler_instance(root_state_dir): version_history = VersionHistory(root_state_dir) state_dir = version_history.code_version.directory - run_tribler_core_session(api_port, api_key, state_dir, gui_test_mode=parsed_args.gui_test_mode) + exit_code = run_tribler_core_session(api_port, api_key, state_dir, gui_test_mode=parsed_args.gui_test_mode) + + if exit_code: + sys.exit(exit_code) diff --git a/src/tribler/gui/core_manager.py b/src/tribler/gui/core_manager.py index 78a4f8a1841..0778ac9025a 100644 --- a/src/tribler/gui/core_manager.py +++ b/src/tribler/gui/core_manager.py @@ -211,11 +211,14 @@ def get_last_core_output(self, quoted=True): @staticmethod def format_error_message(exit_code: int, exit_status: int) -> str: message = f"The Tribler core has unexpectedly finished with exit code {exit_code} and status: {exit_status}." - try: - string_error = os.strerror(exit_code) - except ValueError: - # On platforms where strerror() returns NULL when given an unknown error number, ValueError is raised. - string_error = 'unknown error number' + if exit_code == 1: + string_error = "Application error" + else: + try: + string_error = os.strerror(exit_code) + except ValueError: + # On platforms where strerror() returns NULL when given an unknown error number, ValueError is raised. + string_error = 'unknown error number' message += f'\n\nError message: {string_error}' return message