diff options
| author | AAGaming <aagaming@riseup.net> | 2024-08-06 23:25:39 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-08-06 20:25:39 -0700 |
| commit | 166c7ea8a7ea74d9a61d84ebe16556cec9e7cc83 (patch) | |
| tree | 884b3ec5e5bb4e11189eb0cc865b4896421af450 /backend | |
| parent | ddc807340c6d65949c5ddcd665c77beb79edb38e (diff) | |
| download | decky-loader-166c7ea8a7ea74d9a61d84ebe16556cec9e7cc83.tar.gz decky-loader-166c7ea8a7ea74d9a61d84ebe16556cec9e7cc83.zip | |
Work around account switching failing to open the CEF debugger socket (#668)v3.0.0-pre6
* Work around account switching failing to open the CEF debugger socket
this automates lsof and gdb to force close the socket before steam finishes shutting down (from RegisterForShutdownStart)
* lint
* fix LD_LIBRARY_PATH for gdb
Diffstat (limited to 'backend')
4 files changed, 51 insertions, 3 deletions
diff --git a/backend/decky_loader/localplatform/localplatform.py b/backend/decky_loader/localplatform/localplatform.py index 028eff8f..c7085cd1 100644 --- a/backend/decky_loader/localplatform/localplatform.py +++ b/backend/decky_loader/localplatform/localplatform.py @@ -37,6 +37,9 @@ def get_live_reload() -> bool: def get_keep_systemd_service() -> bool: return os.getenv("KEEP_SYSTEMD_SERVICE", "0") == "1" +def get_use_cef_close_workaround() -> bool: + return ON_LINUX and os.getenv("USE_CEF_CLOSE_WORKAROUND", "1") == "1" + def get_log_level() -> int: return {"CRITICAL": 50, "ERROR": 40, "WARNING": 30, "INFO": 20, "DEBUG": 10}[ os.getenv("LOG_LEVEL", "INFO") diff --git a/backend/decky_loader/localplatform/localplatformlinux.py b/backend/decky_loader/localplatform/localplatformlinux.py index f22cb465..2c92124f 100644 --- a/backend/decky_loader/localplatform/localplatformlinux.py +++ b/backend/decky_loader/localplatform/localplatformlinux.py @@ -1,3 +1,5 @@ +from re import compile +from asyncio import Lock import os, pwd, grp, sys, logging from subprocess import call, run, DEVNULL, PIPE, STDOUT from ..enums import UserType @@ -227,3 +229,39 @@ def get_unprivileged_user() -> str: user = 'deck' return user + +# Works around the CEF debugger TCP socket not closing properly when Steam restarts +# Group 1 is PID, group 2 is FD. this also filters for "steamwebhelper" in the process name. +cef_socket_lsof_regex = compile(r"^p(\d+)(?:\s|.)+csteamwebhelper(?:\s|.)+f(\d+)(?:\s|.)+TST=LISTEN") +close_cef_socket_lock = Lock() + +async def close_cef_socket(): + async with close_cef_socket_lock: + if _get_effective_user_id() != 0: + logger.warn("Can't close CEF socket as Decky isn't running as root.") + return + # Look for anything listening TCP on port 8080 + lsof = run(["lsof", "-F", "-iTCP:8080", "-sTCP:LISTEN"], capture_output=True, text=True) + if lsof.returncode != 0 or len(lsof.stdout) < 1: + logger.error(f"lsof call failed in close_cef_socket! return code: {str(lsof.returncode)}") + return + + lsof_data = cef_socket_lsof_regex.match(lsof.stdout) + + if not lsof_data: + logger.error("lsof regex match failed in close_cef_socket!") + return + + pid = lsof_data.group(1) + fd = lsof_data.group(2) + + logger.info(f"Closing CEF socket with PID {pid} and FD {fd}") + + # Use gdb to inject a close() call for the socket fd into steamwebhelper + gdb_ret = run(["gdb", "--nx", "-p", pid, "--batch", "--eval-command", f"call (int)close({fd})"], env={"LD_LIBRARY_PATH": ""}) + + if gdb_ret.returncode != 0: + logger.error(f"Failed to close CEF socket with gdb! return code: {str(gdb_ret.returncode)}", exc_info=True) + return + + logger.info("CEF socket closed") diff --git a/backend/decky_loader/localplatform/localplatformwin.py b/backend/decky_loader/localplatform/localplatformwin.py index 0724b59e..52ade07c 100644 --- a/backend/decky_loader/localplatform/localplatformwin.py +++ b/backend/decky_loader/localplatform/localplatformwin.py @@ -55,4 +55,7 @@ def get_unprivileged_user() -> str: return os.getenv("UNPRIVILEGED_USER", os.getlogin()) async def restart_webhelper() -> bool: - return True # Stubbed
\ No newline at end of file + return True # Stubbed + +async def close_cef_socket(): + return # Stubbed
\ No newline at end of file diff --git a/backend/decky_loader/utilities.py b/backend/decky_loader/utilities.py index 4850cdef..17226ebc 100644 --- a/backend/decky_loader/utilities.py +++ b/backend/decky_loader/utilities.py @@ -20,9 +20,8 @@ from .browser import PluginInstallRequest, PluginInstallType if TYPE_CHECKING: from .main import PluginManager from .injector import inject_to_tab, get_gamepadui_tab, close_old_tabs, get_tab -from .localplatform.localplatform import ON_WINDOWS from . import helpers -from .localplatform.localplatform import service_stop, service_start, get_home_path, get_username +from .localplatform.localplatform import ON_WINDOWS, service_stop, service_start, get_home_path, get_username, get_use_cef_close_workaround, close_cef_socket class FilePickerObj(TypedDict): file: Path @@ -78,6 +77,7 @@ class Utilities: context.ws.add_route("utilities/get_tab_id", self.get_tab_id) context.ws.add_route("utilities/get_user_info", self.get_user_info) context.ws.add_route("utilities/http_request", self.http_request_legacy) + context.ws.add_route("utilities/close_cef_socket", self.close_cef_socket) context.ws.add_route("utilities/_call_legacy_utility", self._call_legacy_utility) context.web_app.add_routes([ @@ -287,6 +287,10 @@ class Utilities: await service_stop(helpers.SSHD_UNIT) return True + async def close_cef_socket(self): + if get_use_cef_close_workaround(): + await close_cef_socket() + async def filepicker_ls(self, path: str | None = None, include_files: bool = True, |
