diff --git a/src/fenrirscreenreader/core/remoteInstanceRegistry.py b/src/fenrirscreenreader/core/remoteInstanceRegistry.py index 5733f3ab..07d707af 100644 --- a/src/fenrirscreenreader/core/remoteInstanceRegistry.py +++ b/src/fenrirscreenreader/core/remoteInstanceRegistry.py @@ -12,6 +12,7 @@ import time INSTANCE_TIMEOUT_SEC = 30.0 +REGISTRY_DIR_MODE = 0o1777 def get_registry_dir(): @@ -26,8 +27,8 @@ def get_instance_file(pid=None): def write_instance(instance_data): registry_dir = get_registry_dir() - os.makedirs(registry_dir, mode=0o755, exist_ok=True) - os.chmod(registry_dir, 0o755) + os.makedirs(registry_dir, mode=REGISTRY_DIR_MODE, exist_ok=True) + os.chmod(registry_dir, REGISTRY_DIR_MODE) prune_stale_instances() instance_data["updated_at"] = time.time() instance_path = get_instance_file(instance_data.get("pid")) diff --git a/src/fenrirscreenreader/remoteDriver/unixDriver.py b/src/fenrirscreenreader/remoteDriver/unixDriver.py index e7411184..720671cb 100644 --- a/src/fenrirscreenreader/remoteDriver/unixDriver.py +++ b/src/fenrirscreenreader/remoteDriver/unixDriver.py @@ -114,6 +114,17 @@ class driver(remoteDriver): } remoteInstanceRegistry.write_instance(instance_data) + def _try_register_instance(self): + try: + self._register_instance() + return True + except Exception as e: + self.env["runtime"]["DebugManager"].write_debug_out( + "unixDriver could not register instance: " + str(e), + debug.DebugLevel.ERROR, + ) + return False + def _socket_file_matches_socket(self, fenrir_sock, socket_file): try: if os.stat(socket_file).st_uid != os.getuid(): @@ -328,11 +339,11 @@ class driver(remoteDriver): if not self.fenrirSocks: return - self._register_instance() + self._try_register_instance() last_register = time.time() while active.value: if time.time() - last_register > 10.0: - self._register_instance() + self._try_register_instance() last_register = time.time() # Check if the client is still connected and if data is available: diff --git a/tests/unit/test_remote_instance_registry.py b/tests/unit/test_remote_instance_registry.py index 7955665a..1b9aec28 100644 --- a/tests/unit/test_remote_instance_registry.py +++ b/tests/unit/test_remote_instance_registry.py @@ -1,9 +1,24 @@ import json +import stat import time from fenrirscreenreader.core import remoteInstanceRegistry +def test_write_instance_creates_shared_sticky_registry_directory( + tmp_path, monkeypatch +): + registry_dir = tmp_path / "instances" + monkeypatch.setattr( + remoteInstanceRegistry, "get_registry_dir", lambda: str(registry_dir) + ) + + remoteInstanceRegistry.write_instance({"pid": 456}) + + mode = stat.S_IMODE(registry_dir.stat().st_mode) + assert mode == 0o1777 + + def test_write_instance_prunes_stale_registry_files(tmp_path, monkeypatch): monkeypatch.setattr( remoteInstanceRegistry, "get_registry_dir", lambda: str(tmp_path) diff --git a/tests/unit/test_unix_remote_driver.py b/tests/unit/test_unix_remote_driver.py index ea95d816..325aa733 100644 --- a/tests/unit/test_unix_remote_driver.py +++ b/tests/unit/test_unix_remote_driver.py @@ -3,6 +3,30 @@ from unittest.mock import Mock, mock_open, patch from fenrirscreenreader.remoteDriver import unixDriver +def test_watchdog_keeps_serving_when_instance_registration_fails( + mock_environment, +): + driver = unixDriver.driver() + driver.env = mock_environment + driver.fenrirSocks = [Mock()] + active = Mock(value=True) + event_queue = Mock() + + def stop_after_select(*_args): + active.value = False + return ([], [], []) + + with patch.object(driver, "_get_socket_candidates", return_value=[]), patch.object( + driver, "_register_instance", side_effect=PermissionError("denied") + ), patch( + "fenrirscreenreader.remoteDriver.unixDriver.select.select", + side_effect=stop_after_select, + ) as select_call: + driver.watch_dog(active, event_queue) + + select_call.assert_called_once() + + class FakeClientSocket: def __init__(self, data): self.data = data