mirror of
https://github.com/kuhyx/steam-backlog-enforcer.git
synced 2026-11-10 19:07:05 +01:00
fix(enforcer): Steam CDP restart never worked (root+no-display, port collision)
The systemd service runs as root with no User=, so _run_as_user() had no SUDO_USER/USER to drop privileges to and launched Steam as bare root with no DISPLAY/XAUTHORITY/DBUS session - its CDP port never opened. Add an explicit STEAM_ENFORCER_DESKTOP_USER env var (set in the unit, substituted by install.sh) so the enforcer always knows which desktop user owns the Steam session, regardless of how it was launched. Also fix a hardcoded _CDP_PORT=8080 colliding with an already-running local service bound to 0.0.0.0:8080: requests to 127.0.0.1:8080 always hit that service instead of steamwebhelper (which only bound [::1]:8080), so CDP detection could never succeed even with the user fix. Move to 9222, the conventional Chrome DevTools port. Verified live: journalctl shows CDP ready and games hidden within ~7s of a service restart, Steam running as the desktop user (not root).
This commit is contained in:
parent
d54df3a1ad
commit
00a08de076
10
install.sh
10
install.sh
@ -43,8 +43,14 @@ if [[ "${ans,,}" == "y" ]]; then
|
||||
SERVICE_SRC="$SCRIPT_DIR/steam-backlog-enforcer.service"
|
||||
SERVICE_DST="/etc/systemd/system/steam-backlog-enforcer.service"
|
||||
|
||||
# Set the correct working directory and PYTHONPATH in the service file.
|
||||
sed "s|WorkingDirectory=.*|WorkingDirectory=$SCRIPT_DIR|; s|PYTHONPATH=.*|PYTHONPATH=$SCRIPT_DIR|" \
|
||||
# Set the correct working directory, PYTHONPATH, and desktop user (the
|
||||
# user whose Steam/X11 session the service should drive) in the service
|
||||
# file. install.sh requires root, so it's invoked via `sudo` and
|
||||
# SUDO_USER holds the real desktop user.
|
||||
DESKTOP_USER="${SUDO_USER:-$USER}"
|
||||
sed "s|WorkingDirectory=.*|WorkingDirectory=$SCRIPT_DIR|; \
|
||||
s|PYTHONPATH=.*|PYTHONPATH=$SCRIPT_DIR|; \
|
||||
s|__DESKTOP_USER__|$DESKTOP_USER|" \
|
||||
"$SERVICE_SRC" > "$SERVICE_DST"
|
||||
|
||||
systemctl daemon-reload
|
||||
|
||||
@ -12,6 +12,8 @@ RestartSec=5
|
||||
Environment=PYTHONUNBUFFERED=1
|
||||
Environment=PYTHONPATH=/opt/steam-backlog-enforcer
|
||||
Environment=HOME=/home/kuhy
|
||||
Environment=STEAM_ENFORCER_DESKTOP_USER=__DESKTOP_USER__
|
||||
Environment=DISPLAY=:0
|
||||
# Hardening: enforcer must not be easily killed.
|
||||
OOMScoreAdjust=-900
|
||||
|
||||
|
||||
@ -31,7 +31,12 @@ import websockets
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
_CDP_PORT = 8080
|
||||
_CDP_PORT = 9222
|
||||
# NOTE: was 8080, which collided with a different local service (Open WebUI)
|
||||
# already bound to 0.0.0.0:8080. requests to 127.0.0.1:8080 resolved to that
|
||||
# service instead of steamwebhelper's CDP endpoint (which only bound
|
||||
# [::1]:8080), so CDP detection silently never worked. 9222 is the
|
||||
# conventional Chrome DevTools debug port and was confirmed free.
|
||||
_CDP_TIMEOUT = 120
|
||||
_STEAM_STARTUP_WAIT = 45
|
||||
|
||||
@ -183,9 +188,24 @@ def _wait_for_collections_ready() -> bool:
|
||||
return False
|
||||
|
||||
|
||||
def _resolve_desktop_user() -> str | None:
|
||||
"""Resolve which desktop user owns the Steam/X11 session.
|
||||
|
||||
Prefers the explicit STEAM_ENFORCER_DESKTOP_USER (set by the systemd
|
||||
unit, which has no SUDO_USER/USER of its own since it is started
|
||||
directly by systemd rather than via `sudo`), then falls back to
|
||||
SUDO_USER/USER for interactive `sudo` invocations.
|
||||
"""
|
||||
return (
|
||||
os.environ.get("STEAM_ENFORCER_DESKTOP_USER")
|
||||
or os.environ.get("SUDO_USER")
|
||||
or os.environ.get("USER")
|
||||
)
|
||||
|
||||
|
||||
def _shutdown_steam() -> None:
|
||||
"""Send ``steam -shutdown`` and wait for the process to exit."""
|
||||
real_user = os.environ.get("SUDO_USER") or os.environ.get("USER")
|
||||
real_user = _resolve_desktop_user()
|
||||
try:
|
||||
_run_as_user(["steam", "-shutdown"], real_user)
|
||||
except FileNotFoundError:
|
||||
@ -205,7 +225,7 @@ def _shutdown_steam() -> None:
|
||||
|
||||
def _launch_steam_with_debug() -> None:
|
||||
"""Launch Steam with CEF debugging enabled."""
|
||||
real_user = os.environ.get("SUDO_USER") or os.environ.get("USER")
|
||||
real_user = _resolve_desktop_user()
|
||||
_run_as_user(
|
||||
[
|
||||
"steam",
|
||||
|
||||
@ -9,6 +9,7 @@ from unittest.mock import MagicMock, patch
|
||||
from steam_backlog_enforcer.library_hider import (
|
||||
_SPAWNED,
|
||||
_reap_spawned,
|
||||
_resolve_desktop_user,
|
||||
_run_as_user,
|
||||
hide_other_games,
|
||||
restart_steam,
|
||||
@ -19,6 +20,48 @@ from steam_backlog_enforcer.library_hider import (
|
||||
PKG = "steam_backlog_enforcer.library_hider"
|
||||
|
||||
|
||||
class TestResolveDesktopUser:
|
||||
"""Tests for _resolve_desktop_user."""
|
||||
|
||||
def test_prefers_steam_enforcer_desktop_user(self) -> None:
|
||||
"""The systemd unit's explicit var wins over SUDO_USER/USER."""
|
||||
with patch.dict(
|
||||
os.environ,
|
||||
{
|
||||
"STEAM_ENFORCER_DESKTOP_USER": "kuhy",
|
||||
"SUDO_USER": "someone_else",
|
||||
"USER": "root",
|
||||
},
|
||||
):
|
||||
assert _resolve_desktop_user() == "kuhy"
|
||||
|
||||
def test_falls_back_to_sudo_user(self) -> None:
|
||||
"""Interactive `sudo` invocations have no explicit var set."""
|
||||
env = os.environ.copy()
|
||||
env.pop("STEAM_ENFORCER_DESKTOP_USER", None)
|
||||
env["SUDO_USER"] = "alice"
|
||||
env["USER"] = "root"
|
||||
with patch.dict(os.environ, env, clear=True):
|
||||
assert _resolve_desktop_user() == "alice"
|
||||
|
||||
def test_falls_back_to_user(self) -> None:
|
||||
"""A direct, non-sudo invocation has neither var set."""
|
||||
env = os.environ.copy()
|
||||
env.pop("STEAM_ENFORCER_DESKTOP_USER", None)
|
||||
env.pop("SUDO_USER", None)
|
||||
env["USER"] = "kuhy"
|
||||
with patch.dict(os.environ, env, clear=True):
|
||||
assert _resolve_desktop_user() == "kuhy"
|
||||
|
||||
def test_returns_none_when_nothing_set(self) -> None:
|
||||
env = os.environ.copy()
|
||||
env.pop("STEAM_ENFORCER_DESKTOP_USER", None)
|
||||
env.pop("SUDO_USER", None)
|
||||
env.pop("USER", None)
|
||||
with patch.dict(os.environ, env, clear=True):
|
||||
assert _resolve_desktop_user() is None
|
||||
|
||||
|
||||
class TestSteamIsInstalled:
|
||||
"""Tests for steam_is_installed."""
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user