diff --git a/dangerzone/cli.py b/dangerzone/cli.py index 5e19fab..ca5b644 100644 --- a/dangerzone/cli.py +++ b/dangerzone/cli.py @@ -11,6 +11,7 @@ from .isolation_provider.container import Container from .isolation_provider.dummy import Dummy from .isolation_provider.qubes import Qubes, is_qubes_native_conversion from .logic import DangerzoneCore +from .settings import Settings from .util import get_version, replace_control_chars @@ -37,7 +38,7 @@ def print_header(s: str) -> None: ) @click.argument( "filenames", - required=True, + required=False, nargs=-1, type=click.UNPROCESSED, callback=args.validate_input_filenames, @@ -48,17 +49,33 @@ def print_header(s: str) -> None: flag_value=True, help="Run Dangerzone in debug mode, to get logs from gVisor.", ) +@click.option( + "--set-container-runtime", + required=False, + help="The path to the container runtime you want to set in the settings", +) @click.version_option(version=get_version(), message="%(version)s") @errors.handle_document_errors def cli_main( output_filename: Optional[str], ocr_lang: Optional[str], - filenames: List[str], + filenames: Optional[List[str]], archive: bool, dummy_conversion: bool, debug: bool, + set_container_runtime: Optional[str] = None, ) -> None: setup_logging() + display_banner() + if set_container_runtime: + settings = Settings() + container_runtime = settings.set_custom_runtime( + set_container_runtime, autosave=True + ) + click.echo(f"Set the settings container_runtime to {container_runtime}") + sys.exit(0) + elif not filenames: + raise click.UsageError("Missing argument 'FILENAMES...'") if getattr(sys, "dangerzone_dev", False) and dummy_conversion: dangerzone = DangerzoneCore(Dummy()) @@ -67,7 +84,6 @@ def cli_main( else: dangerzone = DangerzoneCore(Container(debug=debug)) - display_banner() if len(filenames) == 1 and output_filename: dangerzone.add_document_from_filename(filenames[0], output_filename, archive) elif len(filenames) > 1 and output_filename: diff --git a/dangerzone/container_utils.py b/dangerzone/container_utils.py index 6101cdd..e7d60ff 100644 --- a/dangerzone/container_utils.py +++ b/dangerzone/container_utils.py @@ -16,6 +16,14 @@ log = logging.getLogger(__name__) class Runtime(object): + """Represents the container runtime to use. + + - It can be specified via the settings, using the "container_runtime" key, + which should point to the full path of the runtime; + - If the runtime is not specified via the settings, it defaults + to "podman" on Linux and "docker" on macOS and Windows. + """ + def __init__(self) -> None: settings = Settings() @@ -26,14 +34,22 @@ class Runtime(object): self.name = self.path.stem else: self.name = self.get_default_runtime_name() - binary_path = shutil.which(self.name) - if binary_path is None or not os.path.exists(binary_path): - raise errors.NoContainerTechException(self.name) - self.path = Path(binary_path) + self.path = Runtime.path_from_name(self.name) if self.name not in ("podman", "docker"): raise errors.UnsupportedContainerRuntime(self.name) + @staticmethod + def path_from_name(name: str) -> Path: + name_path = Path(name) + if name_path.is_file(): + return name_path + else: + runtime = shutil.which(name_path) + if runtime is None: + raise errors.NoContainerTechException(name) + return Path(runtime) + @staticmethod def get_default_runtime_name() -> str: return "podman" if platform.system() == "Linux" else "docker" diff --git a/dangerzone/gui/main_window.py b/dangerzone/gui/main_window.py index 3f6cbf3..38b871f 100644 --- a/dangerzone/gui/main_window.py +++ b/dangerzone/gui/main_window.py @@ -605,17 +605,18 @@ class WaitingWidgetContainer(WaitingWidget): ) elif platform.system() == "Linux": # "not_running" here means that the `podman image ls` command failed. - message = ( + self.show_error( "Dangerzone requires Podman

" - "Podman is installed but cannot run properly. See errors below" + "Podman is installed but cannot run properly. See errors below", + error, ) else: - message = ( + self.show_error( "Dangerzone requires Docker Desktop

" "Docker is installed but isn't running.

" - "Open Docker and make sure it's running in the background." + "Open Docker and make sure it's running in the background.", + error, ) - self.show_error(message, error) else: self.show_message( "Installing the Dangerzone container image.

" diff --git a/dangerzone/settings.py b/dangerzone/settings.py index a10ad6b..a95917b 100644 --- a/dangerzone/settings.py +++ b/dangerzone/settings.py @@ -1,6 +1,7 @@ import json import logging import os +from pathlib import Path from typing import TYPE_CHECKING, Any, Dict from packaging import version @@ -42,6 +43,15 @@ class Settings: def custom_runtime_specified(self) -> bool: return "container_runtime" in self.settings + def set_custom_runtime(self, runtime: str, autosave: bool = False) -> Path: + from .container_utils import Runtime # Avoid circular import + + container_runtime = Runtime.path_from_name(runtime) + self.settings["container_runtime"] = str(container_runtime) + if autosave: + self.save() + return container_runtime + def get(self, key: str) -> Any: return self.settings[key] diff --git a/tests/test_container_utils.py b/tests/test_container_utils.py index e7ee07e..db47b5a 100644 --- a/tests/test_container_utils.py +++ b/tests/test_container_utils.py @@ -49,7 +49,7 @@ def test_get_runtime_name_non_linux(mocker: MockerFixture, tmp_path: Path) -> No assert Runtime().name == "docker" -def test_get_unsupported_runtime_name(mocker: MockerFixture, tmp_path: Path): +def test_get_unsupported_runtime_name(mocker: MockerFixture, tmp_path: Path) -> None: mocker.patch("dangerzone.settings.get_config_dir", return_value=tmp_path) settings = Settings() settings.set(