mod attr;
mod ids;
#[cfg(target_os = "linux")]
mod server;
mod vfs;
use std::net::IpAddr;
use arcbox_connect::v1::EnsureMachineExportRequest;
pub use attr::IdMap;
#[cfg(target_os = "linux")]
pub use server::Endpoint;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ExportConfig {
pub ids: IdMap,
pub client_addresses: Vec<IpAddr>,
}
impl ExportConfig {
pub fn from_request(req: &EnsureMachineExportRequest) -> Result<Self, String> {
if req.client_addresses.is_empty() {
return Err("no client addresses: the export would admit no connection".to_owned());
}
let client_addresses = req
.client_addresses
.iter()
.map(|raw| {
raw.parse::<IpAddr>()
.map_err(|e| format!("client address {raw:?}: {e}"))
})
.collect::<Result<Vec<_>, _>>()?;
Ok(Self {
ids: IdMap::new(req.host_uid, req.host_gid, req.guest_uid, req.guest_gid),
client_addresses,
})
}
}
#[cfg(target_os = "linux")]
pub async fn ensure(
root: std::path::PathBuf,
bridge: std::net::Ipv4Addr,
config: ExportConfig,
) -> Result<Endpoint, String> {
static ENDPOINT: tokio::sync::OnceCell<Endpoint> = tokio::sync::OnceCell::const_new();
ENDPOINT
.get_or_try_init(|| async move {
server::serve(root, bridge, config)
.await
.map_err(|e| format!("start the machine export on {bridge}: {e}"))
})
.await
.copied()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn a_request_names_at_least_one_valid_peer() {
let req = EnsureMachineExportRequest {
client_addresses: vec!["192.168.64.1".to_owned()],
host_uid: 501,
host_gid: 20,
..Default::default()
};
let config = ExportConfig::from_request(&req).unwrap();
assert_eq!(
config.client_addresses,
vec!["192.168.64.1".parse::<IpAddr>().unwrap()]
);
assert_eq!(config.ids, IdMap::new(501, 20, 0, 0));
let empty = EnsureMachineExportRequest::default();
assert!(ExportConfig::from_request(&empty).is_err());
let garbage = EnsureMachineExportRequest {
client_addresses: vec!["bridge100".to_owned()],
..Default::default()
};
assert!(ExportConfig::from_request(&garbage).is_err());
}
}