use std::{
fs::File,
os::fd::{AsFd, BorrowedFd},
};
use aya_obj::{
btf::{Btf, BtfKind},
generated::{
bpf_attach_type::BPF_TRACE_ITER, bpf_link_type::BPF_LINK_TYPE_ITER,
bpf_prog_type::BPF_PROG_TYPE_TRACING,
},
};
use crate::{
programs::{
FdLink, LinkError, PerfLinkIdInner, PerfLinkInner, ProgramData, ProgramError, ProgramType,
define_link_wrapper, impl_try_into_fdlink, load_program,
},
sys::{LinkTarget, SyscallError, bpf_create_iter, bpf_link_create, bpf_link_get_info_by_fd},
};
#[derive(Debug)]
pub struct Iter {
pub(crate) data: ProgramData<IterLink>,
}
impl Iter {
pub const PROGRAM_TYPE: ProgramType = ProgramType::Tracing;
pub fn load(&mut self, iter_type: &str, btf: &Btf) -> Result<(), ProgramError> {
self.data.expected_attach_type = Some(BPF_TRACE_ITER);
let type_name = format!("bpf_iter_{iter_type}");
self.data.attach_btf_id =
Some(btf.id_by_type_name_kind(type_name.as_str(), BtfKind::Func)?);
load_program(BPF_PROG_TYPE_TRACING, &mut self.data)
}
pub fn attach(&mut self) -> Result<IterLinkId, ProgramError> {
let prog_fd = self.fd()?;
let prog_fd = prog_fd.as_fd();
let link_fd = bpf_link_create(prog_fd, LinkTarget::Iter, BPF_TRACE_ITER, 0, None).map_err(
|io_error| SyscallError {
call: "bpf_link_create",
io_error,
},
)?;
self.data
.links
.insert(IterLink::new(PerfLinkInner::Fd(FdLink::new(link_fd))))
}
}
#[derive(Debug)]
pub struct IterFd {
fd: crate::MockableFd,
}
impl AsFd for IterFd {
fn as_fd(&self) -> BorrowedFd<'_> {
let Self { fd } = self;
fd.as_fd()
}
}
impl TryFrom<FdLink> for IterLink {
type Error = LinkError;
fn try_from(fd_link: FdLink) -> Result<Self, Self::Error> {
let info = bpf_link_get_info_by_fd(fd_link.fd.as_fd())?;
if info.type_ == (BPF_LINK_TYPE_ITER as u32) {
return Ok(Self::new(PerfLinkInner::Fd(fd_link)));
}
Err(LinkError::InvalidLink)
}
}
impl_try_into_fdlink!(IterLink, PerfLinkInner);
define_link_wrapper!(IterLink, IterLinkId, PerfLinkInner, PerfLinkIdInner, Iter);
impl IterLink {
pub fn into_file(self) -> Result<File, LinkError> {
if let PerfLinkInner::Fd(fd) = self.into_inner() {
let fd = bpf_create_iter(fd.fd.as_fd()).map_err(|io_error| {
LinkError::SyscallError(SyscallError {
call: "bpf_iter_create",
io_error,
})
})?;
Ok(fd.into_inner().into())
} else {
Err(LinkError::InvalidLink)
}
}
}