hotl_platform/ipc/
unix.rs1use super::{Ipc, IpcListener, Liveness, PeerReject};
4use crate::KnownPaths as _;
5use std::io;
6use std::path::PathBuf;
7
8pub type UnixIpcStream = tokio::net::UnixStream;
9
10#[derive(Debug, Clone, Copy, Default)]
11pub struct UnixIpc;
12
13impl UnixIpc {
14 pub const fn new() -> Self {
15 Self
16 }
17}
18
19impl crate::sealed::Sealed for UnixIpc {}
20
21fn run_dir() -> PathBuf {
23 crate::KNOWN_PATHS
24 .data()
25 .unwrap_or_else(|| PathBuf::from("."))
26 .join("run")
27}
28
29fn sock_path(id: &str) -> PathBuf {
30 run_dir().join(format!("{id}.sock"))
31}
32
33pub struct UnixIpcListener(tokio::net::UnixListener);
34
35impl IpcListener for UnixIpcListener {
36 type Stream = UnixIpcStream;
37
38 async fn accept(&mut self) -> io::Result<Self::Stream> {
39 self.0.accept().await.map(|(s, _)| s)
40 }
41}
42
43impl Ipc for UnixIpc {
44 type Listener = UnixIpcListener;
45 type Stream = UnixIpcStream;
46
47 const LEAVES_STALE_ARTIFACT: bool = true;
51
52 fn bind_private(&self, id: &str) -> io::Result<Self::Listener> {
53 use crate::PrivateFs as _;
54 let dir = run_dir();
55 crate::PRIVATE_FS.create_dir_all(&dir)?;
56 let path = sock_path(id);
57 if path.exists() && self.liveness(id) == Liveness::Dead {
60 let _ = std::fs::remove_file(&path);
61 }
62 let listener = tokio::net::UnixListener::bind(&path)?;
63 crate::PRIVATE_FS.harden_existing(&path)?;
66 Ok(UnixIpcListener(listener))
67 }
68
69 async fn connect(&self, id: &str) -> io::Result<Self::Stream> {
70 tokio::net::UnixStream::connect(sock_path(id)).await
71 }
72
73 fn authenticate_peer(&self, stream: &Self::Stream) -> Result<(), PeerReject> {
74 let cred = stream
77 .peer_cred()
78 .map_err(|e| PeerReject(format!("the peer's credentials are unreadable: {e}")))?;
79 let me = unsafe { libc::getuid() };
81 if cred.uid() != me {
82 return Err(PeerReject(format!(
83 "the peer runs as uid {}, not {me}",
84 cred.uid()
85 )));
86 }
87 Ok(())
88 }
89
90 fn liveness(&self, id: &str) -> Liveness {
91 match std::os::unix::net::UnixStream::connect(sock_path(id)) {
94 Ok(_) => Liveness::Live,
95 Err(_) => Liveness::Dead,
96 }
97 }
98
99 fn list_live(&self) -> Vec<String> {
100 let Ok(entries) = std::fs::read_dir(run_dir()) else {
101 return Vec::new();
102 };
103 entries
104 .flatten()
105 .filter_map(|e| {
106 let p = e.path();
107 if p.extension()? != "sock" {
108 return None;
109 }
110 let id = p.file_stem()?.to_str()?.to_string();
111 (self.liveness(&id) == Liveness::Live).then_some(id)
112 })
113 .collect()
114 }
115
116 fn artifact_path(&self, id: &str) -> Option<PathBuf> {
117 Some(sock_path(id))
118 }
119}