mirror of
https://github.com/SteamDeckHomebrew/decky-loader.git
synced 2026-06-17 08:47:49 +00:00
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:
@@ -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
|
||||||
@@ -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,
|
||||||
|
|||||||
@@ -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());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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();
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user