use std::{
ffi::OsStr,
io,
os::fd::AsFd as _,
path::{Path, PathBuf},
};
use thiserror::Error;
use crate::{
generated::{bpf_link_type, bpf_prog_type::BPF_PROG_TYPE_KPROBE},
programs::{
define_link_wrapper, load_program,
perf_attach::{PerfLinkIdInner, PerfLinkInner},
probe::{attach, ProbeKind},
FdLink, LinkError, ProgramData, ProgramError,
},
sys::bpf_link_get_info_by_fd,
VerifierLogLevel,
};
#[derive(Debug)]
#[doc(alias = "BPF_PROG_TYPE_KPROBE")]
pub struct KProbe {
pub(crate) data: ProgramData<KProbeLink>,
pub(crate) kind: ProbeKind,
}
impl KProbe {
pub fn load(&mut self) -> Result<(), ProgramError> {
load_program(BPF_PROG_TYPE_KPROBE, &mut self.data)
}
pub fn kind(&self) -> ProbeKind {
self.kind
}
pub fn attach<T: AsRef<OsStr>>(
&mut self,
fn_name: T,
offset: u64,
) -> Result<KProbeLinkId, ProgramError> {
attach(&mut self.data, self.kind, fn_name.as_ref(), offset, None)
}
pub fn detach(&mut self, link_id: KProbeLinkId) -> Result<(), ProgramError> {
self.data.links.remove(link_id)
}
pub fn take_link(&mut self, link_id: KProbeLinkId) -> Result<KProbeLink, ProgramError> {
self.data.take_link(link_id)
}
pub fn from_pin<P: AsRef<Path>>(path: P, kind: ProbeKind) -> Result<Self, ProgramError> {
let data = ProgramData::from_pinned_path(path, VerifierLogLevel::default())?;
Ok(Self { data, kind })
}
}
define_link_wrapper!(
KProbeLink,
KProbeLinkId,
PerfLinkInner,
PerfLinkIdInner
);
#[derive(Debug, Error)]
pub enum KProbeError {
#[error("`{filename}`")]
FileError {
filename: PathBuf,
#[source]
io_error: io::Error,
},
}
impl TryFrom<KProbeLink> for FdLink {
type Error = LinkError;
fn try_from(value: KProbeLink) -> Result<Self, Self::Error> {
if let PerfLinkInner::FdLink(fd) = value.into_inner() {
Ok(fd)
} else {
Err(LinkError::InvalidLink)
}
}
}
impl TryFrom<FdLink> for KProbeLink {
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::BPF_LINK_TYPE_KPROBE_MULTI as u32) {
return Ok(Self::new(PerfLinkInner::FdLink(fd_link)));
}
Err(LinkError::InvalidLink)
}
}