use super::{Session, SessionManager};
use sha2::{Digest, Sha256};
use std::{collections::BTreeMap, os::unix::fs::MetadataExt};
impl SessionManager {
pub(crate) fn discovery_inventory(
&self,
cancellation: &crate::cancellation::AgentCancellation,
) -> Result<(Vec<Session>, [u8; 32]), &'static str> {
super::store::validate_session_root(&self.root).map_err(|_| "discovery_unavailable")?;
let mut files = BTreeMap::new();
for (index, entry) in std::fs::read_dir(&self.root)
.map_err(|_| "discovery_unavailable")?
.enumerate()
{
cancellation.check().map_err(|_| "operation_cancelled")?;
if index == 4096 {
return Err("discovery_limit");
}
let entry = entry.map_err(|_| "discovery_unavailable")?;
let metadata = std::fs::symlink_metadata(entry.path()).map_err(|_| "stale_revision")?;
let name = entry.file_name();
let lock = name.to_string_lossy().ends_with(".lock");
files.insert(
name,
(
metadata.dev(),
metadata.ino(),
metadata.mode(),
metadata.uid(),
metadata.nlink(),
if lock { 0 } else { metadata.len() },
if lock { 0 } else { metadata.mtime() },
if lock { 0 } else { metadata.mtime_nsec() },
),
);
}
let mut digest = Sha256::new();
let mut sessions = Vec::new();
for (name, signature) in files {
use std::os::unix::ffi::OsStrExt;
digest.update(name.as_bytes().len().to_le_bytes());
digest.update(name.as_bytes());
digest.update(serde_json::to_vec(&signature).map_err(|_| "discovery_unavailable")?);
let Some(id) = name.to_str().and_then(|name| name.strip_suffix(".jsonl")) else {
continue;
};
if id.len() > 64 || super::validate_session_id(id.to_owned()).is_err() {
return Err("discovery_unavailable");
}
if sessions.len() == 256 {
return Err("discovery_limit");
}
sessions.push(self.open(id).map_err(|_| "discovery_unavailable")?);
}
Ok((sessions, digest.finalize().into()))
}
}