Skip to content

Commit

Permalink
Work around account switching failing to open the CEF debugger socket
Browse files Browse the repository at this point in the history
this automates lsof and gdb to force close the socket before steam finishes shutting down (from RegisterForShutdownStart)
  • Loading branch information
AAGaming00 committed Aug 5, 2024
1 parent 7fc51c8 commit f458fd4
Show file tree
Hide file tree
Showing 6 changed files with 72 additions and 7 deletions.
3 changes: 3 additions & 0 deletions backend/decky_loader/localplatform/localplatform.py
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
38 changes: 38 additions & 0 deletions backend/decky_loader/localplatform/localplatformlinux.py
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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})"])

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")
5 changes: 4 additions & 1 deletion backend/decky_loader/localplatform/localplatformwin.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,7 @@ def get_unprivileged_user() -> str:
return os.getenv("UNPRIVILEGED_USER", os.getlogin())

async def restart_webhelper() -> bool:
return True # Stubbed
return True # Stubbed

async def close_cef_socket():
return # Stubbed
8 changes: 6 additions & 2 deletions backend/decky_loader/utilities.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,8 @@
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
Expand Down Expand Up @@ -78,6 +77,7 @@ def __init__(self, context: PluginManager) -> None:
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([
Expand Down Expand Up @@ -287,6 +287,10 @@ async def stop_ssh(self):
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,
Expand Down
9 changes: 5 additions & 4 deletions frontend/src/steamfixes/index.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
// import reloadFix from './reload';
import restartFix from './restart';
// import restartFix from './restart';
import cefSocketFix from "./socket";

let fixes: Function[] = [];

export function deinitSteamFixes() {
fixes.forEach((deinit) => deinit());
}

export async function initSteamFixes() {
// fixes.push(await reloadFix());
fixes.push(await restartFix());
fixes.push(cefSocketFix());
// fixes.push(await restartFix());
}
16 changes: 16 additions & 0 deletions frontend/src/steamfixes/socket.ts
Original file line number Diff line number Diff line change
@@ -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();
}

0 comments on commit f458fd4

Please sign in to comment.