From 9f99ecdfa82c9644484768f773cb23c5395fa09f Mon Sep 17 00:00:00 2001 From: toorux Date: Sat, 24 Oct 2026 14:07:46 +0800 Subject: [PATCH] Wait for the user manager before starting the UI session --- packaging/install.sh | 2 +- src/apk/steam_bridge.rs | 16 ++++++++++------ src/session.rs | 7 ++++--- tests/test_session_unit.py | 24 ++++++++++++++++++++++++ 4 files changed, 39 insertions(+), 10 deletions(-) create mode 100644 tests/test_session_unit.py diff --git a/packaging/install.sh b/packaging/install.sh index f5c5f71..6c15b42 100755 --- a/packaging/install.sh +++ b/packaging/install.sh @@ -221,7 +221,7 @@ cat > /etc/systemd/system/framely-session.service < Result { let uid = unsafe { libc::geteuid() }; ensure!(uid != 0, "Steam APK wrapper must run as the session user"); let p = PathBuf::from(format!("/run/user/{uid}/framely-apk-steam")); - fs::create_dir_all(&p)?; - let m = fs::symlink_metadata(&p)?; + fs::create_dir_all(&p).with_context(|| format!("Create Steam APK runtime directory {}", p.display()))?; + let m = fs::symlink_metadata(&p).with_context(|| format!("Inspect Steam APK runtime directory {}", p.display()))?; ensure!( m.is_dir() && m.uid() == uid, "Invalid Steam wrapper runtime directory" ); - fs::set_permissions(&p, fs::Permissions::from_mode(0o700))?; + fs::set_permissions(&p, fs::Permissions::from_mode(0o700)) + .with_context(|| format!("Protect Steam APK runtime directory {}", p.display()))?; Ok(p) } fn socket() -> Result { @@ -503,7 +504,8 @@ pub(super) fn start_session() -> Result<()> { .create(true) .truncate(false) .mode(0o600) - .open(dir.join("server.lock"))?; + .open(dir.join("server.lock")) + .with_context(|| format!("Open Steam APK bridge lock {}", dir.join("server.lock").display()))?; ensure!( unsafe { libc::flock(lock.as_raw_fd(), libc::LOCK_EX | libc::LOCK_NB) } == 0, "Steam APK bridge is already running" @@ -516,8 +518,10 @@ pub(super) fn start_session() -> Result<()> { ); fs::remove_file(&path)?; } - let listener = UnixListener::bind(&path)?; - fs::set_permissions(path, fs::Permissions::from_mode(0o600))?; + let listener = UnixListener::bind(&path) + .with_context(|| format!("Bind Steam APK bridge socket {}", path.display()))?; + fs::set_permissions(&path, fs::Permissions::from_mode(0o600)) + .with_context(|| format!("Protect Steam APK bridge socket {}", path.display()))?; std::thread::spawn(move || { let _lock = lock; let workers = Arc::new(std::sync::atomic::AtomicUsize::new(0)); diff --git a/src/session.rs b/src/session.rs index 3596d2b..bd20234 100644 --- a/src/session.rs +++ b/src/session.rs @@ -2526,7 +2526,7 @@ pub fn serve( "UI session must not run as root" ); crate::gamepad::session_started(); - crate::apk::steam_session_started()?; + crate::apk::steam_session_started().context("Initialize Steam APK session bridge")?; crate::apk::lifecycle_started(); let server = Server::http("127.0.0.1:0").map_err(|e| anyhow::anyhow!(e.to_string()))?; let port = server @@ -2542,9 +2542,10 @@ pub fn serve( .map(PathBuf::from) .unwrap_or_else(|| PathBuf::from("/tmp")); let runtime = runtime.join(format!("framely-session-{}", std::process::id())); - fs::create_dir(&runtime)?; + fs::create_dir(&runtime).with_context(|| format!("Create UI runtime directory {}", runtime.display()))?; use std::os::unix::fs::PermissionsExt; - fs::set_permissions(&runtime, fs::Permissions::from_mode(0o700))?; + fs::set_permissions(&runtime, fs::Permissions::from_mode(0o700)) + .with_context(|| format!("Protect UI runtime directory {}", runtime.display()))?; let agent = Arc::new(Agent { watch_access: Mutex::new(crate::cast::watch_access::Access { token: ipc::call(&socket, "cast.watchAccess.get", json!({}))?["token"].as_str().map(str::to_owned), diff --git a/tests/test_session_unit.py b/tests/test_session_unit.py new file mode 100644 index 0000000..10c8b6a --- /dev/null +++ b/tests/test_session_unit.py @@ -0,0 +1,24 @@ +"""Check the actual installed UI unit waits for the user runtime provider.""" +import configparser +import pathlib +import subprocess +import unittest + + +class SessionUnitTests(unittest.TestCase): + def test_session_requires_and_orders_after_user_manager(self): + script = (pathlib.Path(__file__).parents[1] / 'packaging/install.sh').read_text() + body = script.split('cat > /etc/systemd/system/framely-session.service <