use core::ffi::c_void;
use std::collections::HashMap;
use std::ffi::CStr;
use std::ffi::CString;
use std::mem;
use std::path::Path;
use std::ptr;
use std::ptr::NonNull;
use crate::set_print;
use crate::util;
use crate::Btf;
use crate::Error;
use crate::Map;
use crate::OpenMap;
use crate::OpenProgram;
use crate::PrintLevel;
use crate::Program;
use crate::Result;
pub trait AsRawLibbpf {
type LibbpfType;
fn as_libbpf_object(&self) -> NonNull<Self::LibbpfType>;
}
#[derive(Debug)]
pub struct ObjectBuilder {
name: Option<CString>,
pin_root_path: Option<CString>,
opts: libbpf_sys::bpf_object_open_opts,
}
impl Default for ObjectBuilder {
fn default() -> Self {
let opts = libbpf_sys::bpf_object_open_opts {
sz: mem::size_of::<libbpf_sys::bpf_object_open_opts>() as libbpf_sys::size_t,
object_name: ptr::null(),
relaxed_maps: false,
pin_root_path: ptr::null(),
kconfig: ptr::null(),
btf_custom_path: ptr::null(),
kernel_log_buf: ptr::null_mut(),
kernel_log_size: 0,
kernel_log_level: 0,
..Default::default()
};
Self {
name: None,
pin_root_path: None,
opts,
}
}
}
impl ObjectBuilder {
pub fn name<T: AsRef<str>>(&mut self, name: T) -> Result<&mut Self> {
self.name = Some(util::str_to_cstring(name.as_ref())?);
self.opts.object_name = self.name.as_ref().map_or(ptr::null(), |p| p.as_ptr());
Ok(self)
}
pub fn pin_root_path<T: AsRef<Path>>(&mut self, path: T) -> Result<&mut Self> {
self.pin_root_path = Some(util::path_to_cstring(path)?);
self.opts.pin_root_path = self
.pin_root_path
.as_ref()
.map_or(ptr::null(), |p| p.as_ptr());
Ok(self)
}
pub fn relaxed_maps(&mut self, relaxed_maps: bool) -> &mut Self {
self.opts.relaxed_maps = relaxed_maps;
self
}
pub fn debug(&mut self, dbg: bool) -> &mut Self {
if dbg {
set_print(Some((PrintLevel::Debug, |_, s| print!("{s}"))));
} else {
set_print(None);
}
self
}
pub fn opts(&self) -> &libbpf_sys::bpf_object_open_opts {
&self.opts
}
pub fn open_file<P: AsRef<Path>>(&mut self, path: P) -> Result<OpenObject> {
let path_c = util::path_to_cstring(path)?;
let path_ptr = path_c.as_ptr();
let opts = self.opts();
util::create_bpf_entity_checked(|| unsafe {
libbpf_sys::bpf_object__open_file(path_ptr, opts)
})
.and_then(|ptr| unsafe { OpenObject::new(ptr) })
}
pub fn open_memory(&mut self, mem: &[u8]) -> Result<OpenObject> {
let opts = self.opts();
util::create_bpf_entity_checked(|| unsafe {
libbpf_sys::bpf_object__open_mem(
mem.as_ptr() as *const c_void,
mem.len() as libbpf_sys::size_t,
opts,
)
})
.and_then(|ptr| unsafe { OpenObject::new(ptr) })
}
}
#[derive(Debug)]
pub struct OpenObject {
ptr: NonNull<libbpf_sys::bpf_object>,
maps: HashMap<String, OpenMap>,
progs: HashMap<String, OpenProgram>,
}
impl OpenObject {
unsafe fn new(ptr: NonNull<libbpf_sys::bpf_object>) -> Result<Self> {
let mut obj = OpenObject {
ptr,
maps: HashMap::new(),
progs: HashMap::new(),
};
let mut map: *mut libbpf_sys::bpf_map = ptr::null_mut();
loop {
let map_ptr = {
let next_ptr = unsafe { libbpf_sys::bpf_object__next_map(obj.ptr.as_ptr(), map) };
match NonNull::new(next_ptr) {
Some(map_ptr) => map_ptr,
None => break,
}
};
let map_obj = unsafe { OpenMap::new(map_ptr) };
obj.maps.insert(map_obj.name()?.into(), map_obj);
map = map_ptr.as_ptr();
}
let mut prog: *mut libbpf_sys::bpf_program = ptr::null_mut();
loop {
let prog_ptr = {
let next_ptr =
unsafe { libbpf_sys::bpf_object__next_program(obj.ptr.as_ptr(), prog) };
match NonNull::new(next_ptr) {
Some(ptr) => ptr,
None => break,
}
};
let program = unsafe { OpenProgram::new(prog_ptr) }?;
obj.progs.insert(program.name()?.into(), program);
prog = prog_ptr.as_ptr();
}
Ok(obj)
}
pub unsafe fn from_ptr(ptr: NonNull<libbpf_sys::bpf_object>) -> Result<Self> {
unsafe { Self::new(ptr) }
}
pub fn take_ptr(mut self) -> NonNull<libbpf_sys::bpf_object> {
let ptr = {
let Self { ptr, maps, progs } = &mut self;
mem::take(maps);
mem::take(progs);
*ptr
};
mem::forget(self);
ptr
}
pub fn name(&self) -> Result<&str> {
unsafe {
let ptr = libbpf_sys::bpf_object__name(self.ptr.as_ptr());
let err = libbpf_sys::libbpf_get_error(ptr as *const _);
if err != 0 {
return Err(Error::from_raw_os_error(err as i32));
}
CStr::from_ptr(ptr)
.to_str()
.map_err(Error::with_invalid_data)
}
}
pub fn map<T: AsRef<str>>(&self, name: T) -> Option<&OpenMap> {
self.maps.get(name.as_ref())
}
pub fn map_mut<T: AsRef<str>>(&mut self, name: T) -> Option<&mut OpenMap> {
self.maps.get_mut(name.as_ref())
}
pub fn maps_iter(&self) -> impl Iterator<Item = &OpenMap> {
self.maps.values()
}
pub fn maps_iter_mut(&mut self) -> impl Iterator<Item = &mut OpenMap> {
self.maps.values_mut()
}
pub fn prog<T: AsRef<str>>(&self, name: T) -> Option<&OpenProgram> {
self.progs.get(name.as_ref())
}
pub fn prog_mut<T: AsRef<str>>(&mut self, name: T) -> Option<&mut OpenProgram> {
self.progs.get_mut(name.as_ref())
}
pub fn progs_iter(&self) -> impl Iterator<Item = &OpenProgram> {
self.progs.values()
}
pub fn progs_iter_mut(&mut self) -> impl Iterator<Item = &mut OpenProgram> {
self.progs.values_mut()
}
pub fn load(self) -> Result<Object> {
let ret = unsafe { libbpf_sys::bpf_object__load(self.ptr.as_ptr()) };
let () = util::parse_ret(ret)?;
let obj = unsafe { Object::from_ptr(self.take_ptr())? };
Ok(obj)
}
}
impl Drop for OpenObject {
fn drop(&mut self) {
unsafe {
libbpf_sys::bpf_object__close(self.ptr.as_ptr());
}
}
}
#[derive(Debug)]
pub struct Object {
ptr: NonNull<libbpf_sys::bpf_object>,
maps: HashMap<String, Map>,
progs: HashMap<String, Program>,
}
impl Object {
pub unsafe fn from_ptr(ptr: NonNull<libbpf_sys::bpf_object>) -> Result<Self> {
let mut obj = Object {
ptr,
maps: HashMap::new(),
progs: HashMap::new(),
};
let mut map: *mut libbpf_sys::bpf_map = ptr::null_mut();
loop {
let map_ptr = {
let next_ptr = unsafe { libbpf_sys::bpf_object__next_map(obj.ptr.as_ptr(), map) };
match NonNull::new(next_ptr) {
Some(map_ptr) => map_ptr,
None => break,
}
};
if unsafe { libbpf_sys::bpf_map__autocreate(map_ptr.as_ptr()) } {
let map_obj = unsafe { Map::new(map_ptr) }?;
obj.maps.insert(map_obj.name().into(), map_obj);
}
map = map_ptr.as_ptr();
}
let mut prog: *mut libbpf_sys::bpf_program = ptr::null_mut();
loop {
let prog_ptr = {
let next_ptr =
unsafe { libbpf_sys::bpf_object__next_program(obj.ptr.as_ptr(), prog) };
match NonNull::new(next_ptr) {
Some(prog_ptr) => prog_ptr,
None => break,
}
};
let program = unsafe { Program::new(prog_ptr) }?;
obj.progs.insert(program.name().into(), program);
prog = prog_ptr.as_ptr();
}
Ok(obj)
}
pub fn btf(&self) -> Result<Option<Btf<'_>>> {
Btf::from_bpf_object(unsafe { &*self.ptr.as_ptr() })
}
pub fn map<T: AsRef<str>>(&self, name: T) -> Option<&Map> {
self.maps.get(name.as_ref())
}
pub fn map_mut<T: AsRef<str>>(&mut self, name: T) -> Option<&mut Map> {
self.maps.get_mut(name.as_ref())
}
pub fn maps_iter(&self) -> impl Iterator<Item = &Map> {
self.maps.values()
}
pub fn maps_iter_mut(&mut self) -> impl Iterator<Item = &mut Map> {
self.maps.values_mut()
}
pub fn prog<T: AsRef<str>>(&self, name: T) -> Option<&Program> {
self.progs.get(name.as_ref())
}
pub fn prog_mut<T: AsRef<str>>(&mut self, name: T) -> Option<&mut Program> {
self.progs.get_mut(name.as_ref())
}
pub fn progs_iter(&self) -> impl Iterator<Item = &Program> {
self.progs.values()
}
pub fn progs_iter_mut(&mut self) -> impl Iterator<Item = &mut Program> {
self.progs.values_mut()
}
}
impl AsRawLibbpf for Object {
type LibbpfType = libbpf_sys::bpf_object;
fn as_libbpf_object(&self) -> NonNull<Self::LibbpfType> {
self.ptr
}
}
impl Drop for Object {
fn drop(&mut self) {
unsafe {
libbpf_sys::bpf_object__close(self.ptr.as_ptr());
}
}
}