use super::{Ipc, IpcListener, Liveness, PeerReject};
use crate::KnownPaths as _;
use std::io;
use std::path::PathBuf;
pub type UnixIpcStream = tokio::net::UnixStream;
#[derive(Debug, Clone, Copy, Default)]
pub struct UnixIpc;
impl UnixIpc {
pub const fn new() -> Self {
Self
}
}
impl crate::sealed::Sealed for UnixIpc {}
fn run_dir() -> PathBuf {
crate::KNOWN_PATHS
.data()
.unwrap_or_else(|| PathBuf::from("."))
.join("run")
}
fn sock_path(id: &str) -> PathBuf {
run_dir().join(format!("{id}.sock"))
}
pub struct UnixIpcListener(tokio::net::UnixListener);
impl IpcListener for UnixIpcListener {
type Stream = UnixIpcStream;
async fn accept(&mut self) -> io::Result<Self::Stream> {
self.0.accept().await.map(|(s, _)| s)
}
}
impl Ipc for UnixIpc {
type Listener = UnixIpcListener;
type Stream = UnixIpcStream;
const LEAVES_STALE_ARTIFACT: bool = true;
fn bind_private(&self, id: &str) -> io::Result<Self::Listener> {
use crate::PrivateFs as _;
let dir = run_dir();
crate::PRIVATE_FS.create_dir_all(&dir)?;
let path = sock_path(id);
if path.exists() && self.liveness(id) == Liveness::Dead {
let _ = std::fs::remove_file(&path);
}
let listener = tokio::net::UnixListener::bind(&path)?;
crate::PRIVATE_FS.harden_existing(&path)?;
Ok(UnixIpcListener(listener))
}
async fn connect(&self, id: &str) -> io::Result<Self::Stream> {
tokio::net::UnixStream::connect(sock_path(id)).await
}
fn authenticate_peer(&self, stream: &Self::Stream) -> Result<(), PeerReject> {
let cred = stream
.peer_cred()
.map_err(|e| PeerReject(format!("the peer's credentials are unreadable: {e}")))?;
let me = unsafe { libc::getuid() };
if cred.uid() != me {
return Err(PeerReject(format!(
"the peer runs as uid {}, not {me}",
cred.uid()
)));
}
Ok(())
}
fn liveness(&self, id: &str) -> Liveness {
match std::os::unix::net::UnixStream::connect(sock_path(id)) {
Ok(_) => Liveness::Live,
Err(_) => Liveness::Dead,
}
}
fn list_live(&self) -> Vec<String> {
let Ok(entries) = std::fs::read_dir(run_dir()) else {
return Vec::new();
};
entries
.flatten()
.filter_map(|e| {
let p = e.path();
if p.extension()? != "sock" {
return None;
}
let id = p.file_stem()?.to_str()?.to_string();
(self.liveness(&id) == Liveness::Live).then_some(id)
})
.collect()
}
fn artifact_path(&self, id: &str) -> Option<PathBuf> {
Some(sock_path(id))
}
}