Wait for the user manager before starting the UI session

This commit is contained in:
toorux committed 2026-10-24 14:07:46 +08:00
1 parent 9a72a50f1a
commit 9f99ecdfa8
4 files changed
+39 -10

No files matched your search

+1 -1
View File
@@ -221,7 +221,7 @@ cat > /etc/systemd/system/framely-session.service <<EOF
[Unit]
Description=Framely SteamVR UI session
After=framely.service user@$steam_uid.service
Requires=framely.service
Requires=framely.service user@$steam_uid.service
[Service]
User=$steam_user
Environment=HOME=$steam_home
+10 -6
View File
@@ -52,13 +52,14 @@ fn runtime() -> Result<PathBuf> {
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<PathBuf> {
@@ -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));
+4 -3
View File
@@ -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),
+24
View File
@@ -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 <<EOF\n', 1)[1].split('\nEOF', 1)[0]
result = subprocess.run(
['bash', '-eu', '-c', 'steam_uid=1000; steam_user=steamos; steam_home=/home/steamos; root=/var/lib/framely\ncat <<EOF\n' + body + '\nEOF\n'],
capture_output=True, text=True, check=True,
)
unit = configparser.ConfigParser(interpolation=None, strict=False)
unit.read_string(result.stdout)
for dependency in ['framely.service', f'user@{1000}.service']:
self.assertIn(dependency, unit['Unit']['Requires'].split())
self.assertIn(dependency, unit['Unit']['After'].split())
if __name__ == '__main__':
unittest.main()