summaryrefslogtreecommitdiff
path: root/backend/decky_loader
diff options
context:
space:
mode:
authorAAGaming <aagaming@riseup.net>2024-08-06 23:25:39 -0400
committerGitHub <noreply@github.com>2024-08-06 20:25:39 -0700
commit166c7ea8a7ea74d9a61d84ebe16556cec9e7cc83 (patch)
tree884b3ec5e5bb4e11189eb0cc865b4896421af450 /backend/decky_loader
parentddc807340c6d65949c5ddcd665c77beb79edb38e (diff)
downloaddecky-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/decky_loader')
-rw-r--r--backend/decky_loader/localplatform/localplatform.py3
-rw-r--r--backend/decky_loader/localplatform/localplatformlinux.py38
-rw-r--r--backend/decky_loader/localplatform/localplatformwin.py5
-rw-r--r--backend/decky_loader/utilities.py8
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,