1 use std::ffi::c_char;
2 use std::ffi::c_int;
3 use std::ffi::c_void;
4 use std::io;
5 use std::io::Write;
6 use std::mem;
7 use std::sync::Mutex;
8 
9 use crate::util::LazyLock;
10 
11 /// An enum representing the different supported print levels.
12 #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
13 #[repr(u32)]
14 pub enum PrintLevel {
15     /// Print warnings and more severe messages.
16     Warn = libbpf_sys::LIBBPF_WARN,
17     /// Print general information and more severe messages.
18     Info = libbpf_sys::LIBBPF_INFO,
19     /// Print debug information and more severe messages.
20     Debug = libbpf_sys::LIBBPF_DEBUG,
21 }
22 
23 impl From<libbpf_sys::libbpf_print_level> for PrintLevel {
from(level: libbpf_sys::libbpf_print_level) -> Self24     fn from(level: libbpf_sys::libbpf_print_level) -> Self {
25         match level {
26             libbpf_sys::LIBBPF_WARN => Self::Warn,
27             libbpf_sys::LIBBPF_INFO => Self::Info,
28             libbpf_sys::LIBBPF_DEBUG => Self::Debug,
29             // shouldn't happen, but anything unknown becomes the highest level
30             _ => Self::Warn,
31         }
32     }
33 }
34 
35 /// The type of callback functions suitable for being provided to [`set_print`].
36 pub type PrintCallback = fn(PrintLevel, String);
37 
38 /// Mimic the default print functionality of libbpf. This way if the user calls `get_print` when no
39 /// previous callback had been set, with the intention of restoring it, everything will behave as
40 /// expected.
default_callback(_lvl: PrintLevel, msg: String)41 fn default_callback(_lvl: PrintLevel, msg: String) {
42     let _ = io::stderr().write(msg.as_bytes());
43 }
44 
45 // While we can't say that set_print is thread-safe, because we shouldn't assume that of
46 // libbpf_set_print, we should still make sure that things are sane on the rust side of things.
47 // Therefore we are using a lock to keep the log level and the callback in sync.
48 //
49 // We don't do anything that can panic with the lock held, so we'll unconditionally unwrap() when
50 // locking the mutex.
51 //
52 // Note that default print behavior ignores debug messages.
53 static PRINT_CB: LazyLock<Mutex<Option<(PrintLevel, PrintCallback)>>> =
54     LazyLock::new(|| Mutex::new(Some((PrintLevel::Info, default_callback))));
55 
outer_print_cb( level: libbpf_sys::libbpf_print_level, fmtstr: *const c_char, va_list: *mut c_void, ) -> c_int56 extern "C" fn outer_print_cb(
57     level: libbpf_sys::libbpf_print_level,
58     fmtstr: *const c_char,
59     // bindgen generated va_list type varies on different platforms, so just use void pointer
60     // instead. It's safe because this argument is always a pointer.
61     // The pointer of this function would be transmuted and passing to libbpf_set_print below.
62     // See <https://github.com/rust-lang/rust-bindgen/issues/2631>
63     va_list: *mut c_void,
64 ) -> c_int {
65     let level = level.into();
66     if let Some((min_level, func)) = { *PRINT_CB.lock().unwrap() } {
67         if level <= min_level {
68             let msg = match unsafe { vsprintf::vsprintf(fmtstr, va_list) } {
69                 Ok(s) => s,
70                 Err(e) => format!("Failed to parse libbpf output: {e}"),
71             };
72             func(level, msg);
73         }
74     }
75     0 // return value is ignored by libbpf
76 }
77 
78 /// Set a callback to receive log messages from libbpf, instead of printing them to stderr.
79 ///
80 /// # Arguments
81 ///
82 /// * `callback` - Either a tuple `(min_level, function)` where `min_level` is the lowest priority
83 ///   log message to handle, or `None` to disable all printing.
84 ///
85 /// This overrides (and is overridden by) [`ObjectBuilder::debug`][crate::ObjectBuilder::debug]
86 ///
87 /// # Examples
88 ///
89 /// To pass all messages to the `log` crate:
90 ///
91 /// ```
92 /// use libbpf_rs::{PrintLevel, set_print};
93 ///
94 /// fn print_to_log(level: PrintLevel, msg: String) {
95 ///     match level {
96 ///         PrintLevel::Debug => log::debug!("{}", msg),
97 ///         PrintLevel::Info => log::info!("{}", msg),
98 ///         PrintLevel::Warn => log::warn!("{}", msg),
99 ///     }
100 /// }
101 ///
102 /// set_print(Some((PrintLevel::Debug, print_to_log)));
103 /// ```
104 ///
105 /// To disable printing completely:
106 ///
107 /// ```
108 /// use libbpf_rs::set_print;
109 /// set_print(None);
110 /// ```
111 ///
112 /// To temporarliy suppress output:
113 ///
114 /// ```
115 /// use libbpf_rs::set_print;
116 ///
117 /// let prev = set_print(None);
118 /// // do things quietly
119 /// set_print(prev);
120 /// ```
set_print( mut callback: Option<(PrintLevel, PrintCallback)>, ) -> Option<(PrintLevel, PrintCallback)>121 pub fn set_print(
122     mut callback: Option<(PrintLevel, PrintCallback)>,
123 ) -> Option<(PrintLevel, PrintCallback)> {
124     // # Safety
125     // outer_print_cb has the same function signature as libbpf_print_fn_t
126     #[allow(clippy::missing_transmute_annotations)]
127     let real_cb: libbpf_sys::libbpf_print_fn_t =
128         unsafe { Some(mem::transmute(outer_print_cb as *const ())) };
129     let real_cb: libbpf_sys::libbpf_print_fn_t = callback.as_ref().and(real_cb);
130     mem::swap(&mut callback, &mut *PRINT_CB.lock().unwrap());
131     unsafe { libbpf_sys::libbpf_set_print(real_cb) };
132     callback
133 }
134 
135 /// Return the current print callback and level.
136 ///
137 /// # Examples
138 ///
139 /// To temporarily suppress output:
140 ///
141 /// ```
142 /// use libbpf_rs::{get_print, set_print};
143 ///
144 /// let prev = get_print();
145 /// set_print(None);
146 /// // do things quietly
147 /// set_print(prev);
148 /// ```
get_print() -> Option<(PrintLevel, PrintCallback)>149 pub fn get_print() -> Option<(PrintLevel, PrintCallback)> {
150     *PRINT_CB.lock().unwrap()
151 }
152