use core::ffi::c_void;
use std::boxed::Box;
use std::ptr;
use std::slice;
use std::time::Duration;
use crate::*;
struct RingBufferCallback {
cb: Box<dyn FnMut(&[u8]) -> i32>,
}
impl RingBufferCallback {
fn new<F>(cb: F) -> Self
where
F: FnMut(&[u8]) -> i32 + 'static,
{
RingBufferCallback { cb: Box::new(cb) }
}
}
#[derive(Default)]
pub struct RingBufferBuilder {
fd_callbacks: Vec<(i32, RingBufferCallback)>,
}
impl RingBufferBuilder {
pub fn new() -> Self {
RingBufferBuilder {
fd_callbacks: vec![],
}
}
pub fn add<NewF>(&mut self, map: &Map, callback: NewF) -> Result<&mut Self>
where
NewF: FnMut(&[u8]) -> i32 + 'static,
{
if map.map_type() != MapType::RingBuf {
return Err(Error::InvalidInput("Must use a RingBuf map".into()));
}
self.fd_callbacks
.push((map.fd(), RingBufferCallback::new(callback)));
Ok(self)
}
pub fn build(self) -> Result<RingBuffer> {
let mut cbs = vec![];
let mut ptr: *mut libbpf_sys::ring_buffer = ptr::null_mut();
let c_sample_cb: libbpf_sys::ring_buffer_sample_fn = Some(Self::call_sample_cb);
for (fd, callback) in self.fd_callbacks {
let sample_cb_ptr = Box::into_raw(Box::new(callback));
if ptr.is_null() {
ptr = unsafe {
libbpf_sys::ring_buffer__new(
fd,
c_sample_cb,
sample_cb_ptr as *mut _,
std::ptr::null_mut(),
)
};
let err = unsafe { libbpf_sys::libbpf_get_error(ptr as *const _) };
if err != 0 {
return Err(Error::System(err as i32));
}
} else {
let err = unsafe {
libbpf_sys::ring_buffer__add(ptr, fd, c_sample_cb, sample_cb_ptr as *mut _)
};
if err != 0 {
return Err(Error::System(err as i32));
}
}
unsafe { cbs.push(Box::from_raw(sample_cb_ptr)) };
}
if ptr.is_null() {
return Err(Error::InvalidInput(
"You must add at least one ring buffer map and callback before building".into(),
));
}
Ok(RingBuffer { ptr, _cbs: cbs })
}
unsafe extern "C" fn call_sample_cb(ctx: *mut c_void, data: *mut c_void, size: u64) -> i32 {
let callback_struct = ctx as *mut RingBufferCallback;
let callback = (*callback_struct).cb.as_mut();
callback(slice::from_raw_parts(data as *const u8, size as usize))
}
}
pub struct RingBuffer {
ptr: *mut libbpf_sys::ring_buffer,
#[allow(clippy::vec_box)]
_cbs: Vec<Box<RingBufferCallback>>,
}
impl RingBuffer {
pub fn poll(&self, timeout: Duration) -> Result<()> {
assert!(!self.ptr.is_null());
let ret = unsafe { libbpf_sys::ring_buffer__poll(self.ptr, timeout.as_millis() as i32) };
if ret < 0 {
Err(Error::System(-ret))
} else {
Ok(())
}
}
pub fn consume(&self) -> Result<()> {
assert!(!self.ptr.is_null());
let ret = unsafe { libbpf_sys::ring_buffer__consume(self.ptr) };
if ret < 0 {
Err(Error::System(-ret))
} else {
Ok(())
}
}
}
impl Drop for RingBuffer {
fn drop(&mut self) {
unsafe {
if !self.ptr.is_null() {
libbpf_sys::ring_buffer__free(self.ptr);
}
}
}
}