steamroom_cli/daemon/
ipc.rs1use std::time::Duration;
5
6use interprocess::local_socket::GenericNamespaced;
7use interprocess::local_socket::ListenerOptions;
8use interprocess::local_socket::Name;
9use interprocess::local_socket::ToNsName;
10use interprocess::local_socket::tokio::Listener;
11use interprocess::local_socket::tokio::Stream;
12use interprocess::local_socket::traits::tokio::Listener as _;
13use interprocess::local_socket::traits::tokio::Stream as _;
14
15use crate::daemon::framing::read_frame;
16use crate::daemon::framing::write_frame;
17use crate::daemon::proto::Frame;
18use crate::daemon::proto::Request;
19use crate::daemon::proto::Response;
20use crate::errors::CliError;
21
22pub fn socket_name() -> Result<Name<'static>, CliError> {
24 let raw = socket_name_string();
25 raw.to_ns_name::<GenericNamespaced>().map_err(CliError::Io)
26}
27
28pub fn socket_name_string() -> String {
29 #[cfg(unix)]
30 {
31 let uid = unsafe { libc::getuid() };
33 format!("steamroom-{uid}.sock")
34 }
35 #[cfg(windows)]
36 {
37 let user = std::env::var("USERNAME").unwrap_or_else(|_| "user".into());
38 format!("steamroom-{user}")
39 }
40}
41
42pub async fn probe_peer() -> Result<(), CliError> {
46 let name = socket_name()?;
47 let mut stream = Stream::connect(name).await.map_err(CliError::Io)?;
48 write_frame(&mut stream, &Frame::Request(Request::Status)).await?;
49 let fut = read_frame(&mut stream);
50 let resp = tokio::time::timeout(Duration::from_millis(200), fut)
51 .await
52 .map_err(|_| {
53 CliError::Io(std::io::Error::new(
54 std::io::ErrorKind::TimedOut,
55 "probe timed out",
56 ))
57 })??;
58 match resp {
59 Frame::Response(Response::Status(_)) => Ok(()),
60 other => Err(CliError::MalformedFrame(format!(
61 "probe expected Status, got {other:?}"
62 ))),
63 }
64}
65
66pub async fn bind_listener() -> Result<Listener, CliError> {
69 if probe_peer().await.is_ok() {
70 return Err(CliError::DaemonAlreadyRunning);
71 }
72 let name = socket_name()?;
73 ListenerOptions::new()
74 .name(name)
75 .try_overwrite(true)
78 .create_tokio()
79 .map_err(CliError::Io)
80}
81
82pub async fn accept(listener: &Listener) -> Result<Stream, CliError> {
83 listener.accept().await.map_err(CliError::Io)
84}