use core::ffi::c_void;
use std::collections::HashMap;
use std::ffi::CStr;
use std::mem;
use std::os::raw::c_char;
use std::path::Path;
use std::ptr;
use nix::errno;
use crate::util;
use crate::*;
pub struct ObjectBuilder {
name: String,
relaxed_maps: bool,
}
impl ObjectBuilder {
pub fn name<T: AsRef<str>>(&mut self, name: T) -> &mut Self {
self.name = name.as_ref().to_string();
self
}
pub fn relaxed_maps(&mut self, relaxed_maps: bool) -> &mut Self {
self.relaxed_maps = relaxed_maps;
self
}
pub fn debug(&mut self, dbg: bool) -> &mut Self {
extern "C" fn cb(
_level: libbpf_sys::libbpf_print_level,
fmtstr: *const c_char,
va_list: *mut libbpf_sys::__va_list_tag,
) -> i32 {
match unsafe { vsprintf::vsprintf(fmtstr, va_list) } {
Ok(s) => {
print!("{}", s);
0
}
Err(e) => {
eprintln!("Failed to parse libbpf output: {}", e);
1
}
}
}
if dbg {
unsafe { libbpf_sys::libbpf_set_print(Some(cb)) };
} else {
unsafe { libbpf_sys::libbpf_set_print(None) };
}
self
}
#[doc(hidden)]
pub fn opts(&mut self, name: *const c_char) -> libbpf_sys::bpf_object_open_opts {
libbpf_sys::bpf_object_open_opts {
sz: mem::size_of::<libbpf_sys::bpf_object_open_opts>() as libbpf_sys::size_t,
object_name: name,
relaxed_maps: self.relaxed_maps,
relaxed_core_relocs: false,
pin_root_path: ptr::null(),
attach_prog_fd: 0,
kconfig: ptr::null(),
}
}
pub fn open_file<P: AsRef<Path>>(&mut self, path: P) -> Result<OpenObject> {
let path_str = path.as_ref().to_str().ok_or_else(|| {
Error::InvalidInput(format!("{} is not valid unicode", path.as_ref().display()))
})?;
let path_c = util::str_to_cstring(path_str)?;
let path_ptr = path_c.as_ptr();
let name = util::str_to_cstring(&self.name)?;
let name_ptr = if !self.name.is_empty() {
name.as_ptr()
} else {
ptr::null()
};
let opts = self.opts(name_ptr);
let obj = unsafe { libbpf_sys::bpf_object__open_file(path_ptr, &opts) };
let err = unsafe { libbpf_sys::libbpf_get_error(obj as *const _) };
if err != 0 {
return Err(Error::System(err as i32));
}
Ok(OpenObject::new(obj))
}
pub fn open_memory<T: AsRef<str>>(&mut self, name: T, mem: &[u8]) -> Result<OpenObject> {
let name = util::str_to_cstring(name.as_ref())?;
let name_ptr = if !name.to_bytes().is_empty() {
name.as_ptr()
} else {
ptr::null()
};
let opts = self.opts(name_ptr);
let obj = unsafe {
libbpf_sys::bpf_object__open_mem(
mem.as_ptr() as *const c_void,
mem.len() as libbpf_sys::size_t,
&opts,
)
};
let err = unsafe { libbpf_sys::libbpf_get_error(obj as *const _) };
if err != 0 {
return Err(Error::System(err as i32));
}
Ok(OpenObject::new(obj))
}
}
impl Default for ObjectBuilder {
fn default() -> Self {
ObjectBuilder {
name: String::new(),
relaxed_maps: false,
}
}
}
fn find_map_in_object(
obj: *const libbpf_sys::bpf_object,
name: &str,
) -> Result<Option<*mut libbpf_sys::bpf_map>> {
let c_name = util::str_to_cstring(name)?;
let ptr = unsafe { libbpf_sys::bpf_object__find_map_by_name(obj, c_name.as_ptr()) };
Ok(util::ptr_to_option(ptr))
}
fn find_prog_in_object(
obj: *const libbpf_sys::bpf_object,
name: &str,
) -> Result<Option<*mut libbpf_sys::bpf_program>> {
let c_name = util::str_to_cstring(name)?;
let ptr = unsafe { libbpf_sys::bpf_object__find_program_by_name(obj, c_name.as_ptr()) };
Ok(util::ptr_to_option(ptr))
}
pub struct OpenObject {
ptr: *mut libbpf_sys::bpf_object,
maps: HashMap<String, OpenMap>,
progs: HashMap<String, OpenProgram>,
}
impl OpenObject {
fn new(ptr: *mut libbpf_sys::bpf_object) -> Self {
OpenObject {
ptr,
maps: HashMap::new(),
progs: HashMap::new(),
}
}
pub unsafe fn from_ptr(ptr: *mut libbpf_sys::bpf_object) -> Self {
Self::new(ptr)
}
pub fn take_ptr(mut self) -> *mut libbpf_sys::bpf_object {
let ptr = self.ptr;
self.ptr = ptr::null_mut();
ptr
}
pub fn name<'a>(&'a self) -> Result<&'a str> {
unsafe {
let ptr = libbpf_sys::bpf_object__name(self.ptr);
let err = libbpf_sys::libbpf_get_error(ptr as *const _);
if err != 0 {
return Err(Error::System(err as i32));
}
CStr::from_ptr(ptr)
.to_str()
.map_err(|e| Error::Internal(e.to_string()))
}
}
pub fn map<T: AsRef<str>>(&mut self, name: T) -> Result<Option<&mut OpenMap>> {
if self.maps.contains_key(name.as_ref()) {
Ok(self.maps.get_mut(name.as_ref()))
} else if let Some(ptr) = find_map_in_object(self.ptr, name.as_ref())? {
self.maps
.insert(name.as_ref().to_owned(), OpenMap::new(ptr));
Ok(self.maps.get_mut(name.as_ref()))
} else {
Ok(None)
}
}
pub fn map_unwrap<T: AsRef<str>>(&mut self, name: T) -> &mut OpenMap {
self.map(name).unwrap().unwrap()
}
pub fn prog<T: AsRef<str>>(&mut self, name: T) -> Result<Option<&mut OpenProgram>> {
if self.progs.contains_key(name.as_ref()) {
Ok(self.progs.get_mut(name.as_ref()))
} else if let Some(ptr) = find_prog_in_object(self.ptr, name.as_ref())? {
let owned_name = name.as_ref().to_owned();
self.progs.insert(owned_name, OpenProgram::new(ptr));
Ok(self.progs.get_mut(name.as_ref()))
} else {
Ok(None)
}
}
pub fn prog_unwrap<T: AsRef<str>>(&mut self, name: T) -> &mut OpenProgram {
self.prog(name).unwrap().unwrap()
}
pub fn load(mut self) -> Result<Object> {
let ret = unsafe { libbpf_sys::bpf_object__load(self.ptr) };
if ret != 0 {
return Err(Error::System(-ret));
}
let obj = Object::new(self.ptr);
self.ptr = ptr::null_mut();
Ok(obj)
}
}
impl Drop for OpenObject {
fn drop(&mut self) {
unsafe {
libbpf_sys::bpf_object__close(self.ptr);
}
}
}
pub struct Object {
ptr: *mut libbpf_sys::bpf_object,
maps: HashMap<String, Map>,
progs: HashMap<String, Program>,
}
impl Object {
fn new(ptr: *mut libbpf_sys::bpf_object) -> Self {
Object {
ptr,
maps: HashMap::new(),
progs: HashMap::new(),
}
}
pub unsafe fn from_ptr(ptr: *mut libbpf_sys::bpf_object) -> Self {
Self::new(ptr)
}
pub fn map<T: AsRef<str>>(&mut self, name: T) -> Result<Option<&mut Map>> {
if self.maps.contains_key(name.as_ref()) {
Ok(self.maps.get_mut(name.as_ref()))
} else if let Some(ptr) = find_map_in_object(self.ptr, name.as_ref())? {
let owned_name = name.as_ref().to_owned();
let fd = unsafe { libbpf_sys::bpf_map__fd(ptr) };
if fd < 0 {
Err(Error::System(errno::errno()))
} else {
let def = unsafe { ptr::read(libbpf_sys::bpf_map__def(ptr)) };
self.maps.insert(
owned_name.clone(),
Map::new(fd, owned_name, def.type_, def.key_size, def.value_size, ptr),
);
Ok(self.maps.get_mut(name.as_ref()))
}
} else {
Ok(None)
}
}
pub fn map_unwrap<T: AsRef<str>>(&mut self, name: T) -> &mut Map {
self.map(name).unwrap().unwrap()
}
pub fn prog<T: AsRef<str>>(&mut self, name: T) -> Result<Option<&mut Program>> {
if self.progs.contains_key(name.as_ref()) {
Ok(self.progs.get_mut(name.as_ref()))
} else if let Some(ptr) = find_prog_in_object(self.ptr, name.as_ref())? {
let owned_name = name.as_ref().to_owned();
let title = unsafe { libbpf_sys::bpf_program__title(ptr, false) };
let err = unsafe { libbpf_sys::libbpf_get_error(title as *const _) };
if err != 0 {
return Err(Error::System(err as i32));
}
let section = util::c_ptr_to_string(title)?;
self.progs
.insert(owned_name.clone(), Program::new(ptr, owned_name, section));
Ok(self.progs.get_mut(name.as_ref()))
} else {
Ok(None)
}
}
pub fn prog_unwrap<T: AsRef<str>>(&mut self, name: T) -> &mut Program {
self.prog(name).unwrap().unwrap()
}
}
impl Drop for Object {
fn drop(&mut self) {
unsafe {
libbpf_sys::bpf_object__close(self.ptr);
}
}
}