use std::path::Path;
use tokio::net::UnixStream;
use super::codec::{read_frame, write_frame};
use super::protocol::{ControlRequest, ControlResponse, PathRuleWire, ProbeSet};
use crate::error::EbpfError;
pub struct LoaderControlClient {
stream: UnixStream,
}
impl LoaderControlClient {
pub async fn connect(path: impl AsRef<Path>) -> Result<Self, EbpfError> {
let stream = UnixStream::connect(path.as_ref()).await?;
Ok(Self { stream })
}
async fn request(&mut self, req: &ControlRequest) -> Result<ControlResponse, EbpfError> {
write_frame(&mut self.stream, req).await?;
read_frame(&mut self.stream)
.await?
.ok_or_else(|| EbpfError::EventParse("control connection closed before response".into()))
}
fn into_result(resp: ControlResponse) -> Result<(), EbpfError> {
match resp {
ControlResponse::Ok | ControlResponse::Pong => Ok(()),
ControlResponse::Error { message } => Err(EbpfError::ProgramLoad(message)),
}
}
pub async fn load_probe_set(&mut self, set: ProbeSet, target_pid: u32) -> Result<(), EbpfError> {
let resp = self.request(&ControlRequest::LoadProbeSet { set, target_pid }).await?;
Self::into_result(resp)
}
pub async fn update_path_map(&mut self, rules: Vec<PathRuleWire>) -> Result<(), EbpfError> {
let resp = self.request(&ControlRequest::UpdatePathMap { rules }).await?;
Self::into_result(resp)
}
pub async fn update_syscall_allowlist(&mut self, syscalls: Vec<u32>) -> Result<(), EbpfError> {
let resp = self
.request(&ControlRequest::UpdateSyscallAllowlist { syscalls })
.await?;
Self::into_result(resp)
}
pub async fn detach(&mut self, set: ProbeSet) -> Result<(), EbpfError> {
let resp = self.request(&ControlRequest::Detach { set }).await?;
Self::into_result(resp)
}
pub async fn ping(&mut self) -> Result<(), EbpfError> {
let resp = self.request(&ControlRequest::Ping).await?;
Self::into_result(resp)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn error_response_maps_to_err() {
let err = LoaderControlClient::into_result(ControlResponse::Error {
message: "unauthorized".into(),
});
assert!(err.is_err());
}
#[test]
fn ok_and_pong_map_to_ok() {
assert!(LoaderControlClient::into_result(ControlResponse::Ok).is_ok());
assert!(LoaderControlClient::into_result(ControlResponse::Pong).is_ok());
}
}