use std::ffi::c_char;
use std::ffi::c_int;
use std::ffi::c_void;
use std::io;
use std::io::Write;
use std::mem;
use std::sync::Mutex;
use crate::util::LazyLock;
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
#[repr(u32)]
pub enum PrintLevel {
Warn = libbpf_sys::LIBBPF_WARN,
Info = libbpf_sys::LIBBPF_INFO,
Debug = libbpf_sys::LIBBPF_DEBUG,
}
impl From<libbpf_sys::libbpf_print_level> for PrintLevel {
fn from(level: libbpf_sys::libbpf_print_level) -> Self {
match level {
libbpf_sys::LIBBPF_WARN => Self::Warn,
libbpf_sys::LIBBPF_INFO => Self::Info,
libbpf_sys::LIBBPF_DEBUG => Self::Debug,
_ => Self::Warn,
}
}
}
pub type PrintCallback = fn(PrintLevel, String);
fn default_callback(_lvl: PrintLevel, msg: String) {
let _count = io::stderr().write(msg.as_bytes());
}
static PRINT_CB: LazyLock<Mutex<Option<(PrintLevel, PrintCallback)>>> =
LazyLock::new(|| Mutex::new(Some((PrintLevel::Info, default_callback))));
extern "C" fn outer_print_cb(
level: libbpf_sys::libbpf_print_level,
fmtstr: *const c_char,
va_list: *mut c_void,
) -> c_int {
let level = level.into();
if let Some((min_level, func)) = { *PRINT_CB.lock().unwrap() } {
if level <= min_level {
let msg = match unsafe { vsprintf::vsprintf(fmtstr, va_list) } {
Ok(s) => s,
Err(e) => format!("Failed to parse libbpf output: {e}"),
};
func(level, msg);
}
}
0 }
pub fn set_print(
mut callback: Option<(PrintLevel, PrintCallback)>,
) -> Option<(PrintLevel, PrintCallback)> {
#[expect(clippy::missing_transmute_annotations)]
let real_cb: libbpf_sys::libbpf_print_fn_t =
unsafe { Some(mem::transmute(outer_print_cb as *const ())) };
let real_cb: libbpf_sys::libbpf_print_fn_t = callback.as_ref().and(real_cb);
mem::swap(&mut callback, &mut *PRINT_CB.lock().unwrap());
unsafe { libbpf_sys::libbpf_set_print(real_cb) };
callback
}
pub fn get_print() -> Option<(PrintLevel, PrintCallback)> {
*PRINT_CB.lock().unwrap()
}