84 lines
2.5 KiB
Python
84 lines
2.5 KiB
Python
"""Identify the host OS without losing the launcher's execution runtime."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from dataclasses import dataclass
|
|
from pathlib import Path
|
|
import re
|
|
|
|
|
|
EXECUTION_OS_RELEASE = Path("/etc/os-release")
|
|
CONTAINER_HOST_OS_RELEASE = Path("/run/host/os-release")
|
|
STEAM_RUNTIME_OS_ID = "steamrt"
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class SystemIdentity:
|
|
"""OS releases visible to the launcher process and its container host."""
|
|
|
|
execution_release: dict[str, str]
|
|
host_release: dict[str, str] | None
|
|
|
|
@property
|
|
def operating_system_release(self) -> dict[str, str]:
|
|
return self.host_release or self.execution_release
|
|
|
|
@property
|
|
def running_inside_steam_runtime(self) -> bool:
|
|
return (
|
|
self.execution_release.get("ID", "").strip().casefold()
|
|
== STEAM_RUNTIME_OS_ID
|
|
)
|
|
|
|
|
|
def detect_system_identity(
|
|
*,
|
|
execution_os_release: Path = EXECUTION_OS_RELEASE,
|
|
host_os_release: Path = CONTAINER_HOST_OS_RELEASE,
|
|
) -> SystemIdentity:
|
|
"""Read the runtime OS and the standard container-host identity, if present."""
|
|
|
|
execution = read_os_release(execution_os_release)
|
|
host: dict[str, str] | None = None
|
|
if execution.get("ID", "").strip().casefold() == STEAM_RUNTIME_OS_ID:
|
|
candidate = read_os_release(host_os_release)
|
|
candidate_id = candidate.get("ID", "").strip().casefold()
|
|
if candidate_id and candidate_id != STEAM_RUNTIME_OS_ID:
|
|
host = candidate
|
|
return SystemIdentity(execution_release=execution, host_release=host)
|
|
|
|
|
|
def detect_distribution_id() -> str:
|
|
"""Return the host distribution ID when a container exposes it."""
|
|
|
|
release = detect_system_identity().operating_system_release
|
|
return release.get("ID", "").strip().casefold()
|
|
|
|
|
|
def is_running_inside_steam_runtime() -> bool:
|
|
"""Return whether the launcher process itself is inside Steam Runtime."""
|
|
|
|
return detect_system_identity().running_inside_steam_runtime
|
|
|
|
|
|
def read_os_release(path: Path) -> dict[str, str]:
|
|
"""Read bounded freedesktop os-release fields without invoking host tools."""
|
|
|
|
try:
|
|
lines = path.read_text(encoding="utf-8", errors="replace").splitlines()
|
|
except OSError:
|
|
return {}
|
|
result: dict[str, str] = {}
|
|
for line in lines:
|
|
if not line or line.startswith("#") or "=" not in line:
|
|
continue
|
|
key, raw = line.split("=", 1)
|
|
if not re.fullmatch(r"[A-Z][A-Z0-9_]*", key):
|
|
continue
|
|
value = raw.strip()
|
|
if len(value) >= 2 and value[0] == value[-1] and value[0] in {"'", '"'}:
|
|
value = value[1:-1]
|
|
value = value.replace(r"\n", " ").replace(r"\"", '"').replace(r"\\", "\\")
|
|
result[key] = value[:255]
|
|
return result
|