Work around account switching failing to open the CEF debugger socket (#668)

* 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
This commit is contained in:
AAGaming
2024-08-06 23:25:39 -04:00
committed by GitHub
parent ddc807340c
commit 166c7ea8a7
6 changed files with 72 additions and 7 deletions
@@ -37,6 +37,9 @@ def get_live_reload() -> bool:
def get_keep_systemd_service() -> bool: def get_keep_systemd_service() -> bool:
return os.getenv("KEEP_SYSTEMD_SERVICE", "0") == "1" 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: def get_log_level() -> int:
return {"CRITICAL": 50, "ERROR": 40, "WARNING": 30, "INFO": 20, "DEBUG": 10}[ return {"CRITICAL": 50, "ERROR": 40, "WARNING": 30, "INFO": 20, "DEBUG": 10}[
os.getenv("LOG_LEVEL", "INFO") os.getenv("LOG_LEVEL", "INFO")
@@ -1,3 +1,5 @@
from re import compile
from asyncio import Lock
import os, pwd, grp, sys, logging import os, pwd, grp, sys, logging
from subprocess import call, run, DEVNULL, PIPE, STDOUT from subprocess import call, run, DEVNULL, PIPE, STDOUT
from ..enums import UserType from ..enums import UserType
@@ -227,3 +229,39 @@ def get_unprivileged_user() -> str:
user = 'deck' user = 'deck'
return user 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")
@@ -55,4 +55,7 @@ def get_unprivileged_user() -> str:
return os.getenv("UNPRIVILEGED_USER", os.getlogin()) return os.getenv("UNPRIVILEGED_USER", os.getlogin())
async def restart_webhelper() -> bool: async def restart_webhelper() -> bool:
return True # Stubbed return True # Stubbed
async def close_cef_socket():
return # Stubbed
+6 -2
View File
@@ -20,9 +20,8 @@ from .browser import PluginInstallRequest, PluginInstallType
if TYPE_CHECKING: if TYPE_CHECKING:
from .main import PluginManager from .main import PluginManager
from .injector import inject_to_tab, get_gamepadui_tab, close_old_tabs, get_tab from .injector import inject_to_tab, get_gamepadui_tab, close_old_tabs, get_tab
from .localplatform.localplatform import ON_WINDOWS
from . import helpers 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): class FilePickerObj(TypedDict):
file: Path 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_tab_id", self.get_tab_id)
context.ws.add_route("utilities/get_user_info", self.get_user_info) 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/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.ws.add_route("utilities/_call_legacy_utility", self._call_legacy_utility)
context.web_app.add_routes([ context.web_app.add_routes([
@@ -287,6 +287,10 @@ class Utilities:
await service_stop(helpers.SSHD_UNIT) await service_stop(helpers.SSHD_UNIT)
return True return True
async def close_cef_socket(self):
if get_use_cef_close_workaround():
await close_cef_socket()
async def filepicker_ls(self, async def filepicker_ls(self,
path: str | None = None, path: str | None = None,
include_files: bool = True, include_files: bool = True,
+5 -4
View File
@@ -1,5 +1,6 @@
// import reloadFix from './reload'; // import restartFix from './restart';
import restartFix from './restart'; import cefSocketFix from './socket';
let fixes: Function[] = []; let fixes: Function[] = [];
export function deinitSteamFixes() { export function deinitSteamFixes() {
@@ -7,6 +8,6 @@ export function deinitSteamFixes() {
} }
export async function initSteamFixes() { export async function initSteamFixes() {
// fixes.push(await reloadFix()); fixes.push(cefSocketFix());
fixes.push(await restartFix()); // fixes.push(await restartFix());
} }
+16
View File
@@ -0,0 +1,16 @@
import Logger from '../logger';
const logger = new Logger('CEFSocketFix');
const closeCEFSocket = DeckyBackend.callable<[], void>('utilities/close_cef_socket');
export default function cefSocketFix() {
const reg = window.SteamClient?.User?.RegisterForShutdownStart(async () => {
logger.log('Closing CEF socket before shutdown');
await closeCEFSocket();
});
if (reg) logger.debug('CEF shutdown handler ready');
return () => reg?.unregister();
}