-/* Copyright (C) 2017 Open Information Security Foundation
+/* Copyright (C) 2017-2025 Open Information Security Foundation
*
* You can copy, redistribute or modify this Program under the terms of
* the GNU General Public License version 2 as published by the Free
* 02110-1301, USA.
*/
-//! Logging utility module.
+//! Logging and debug utilities, like util-debug.c.
-use std;
-use std::ffi::CString;
-use std::path::Path;
+use std::{ffi::CString, path::Path};
-use crate::core::*;
+use crate::core::SC;
+
+/// cbindgen:ignore
+extern {
+ pub fn SCLogGetLogLevel() -> i32;
+}
#[derive(Debug)]
#[repr(C)]
pub static mut LEVEL: i32 = Level::NotSet as i32;
-pub fn get_log_level() -> i32 {
- unsafe {
- LEVEL
- }
-}
-
-pub fn log_set_level(level: i32) {
- unsafe {
- LEVEL = level;
- }
-}
-
+/// Set the Rust context's idea of the log level.
+///
+/// This will be called during Suricata initialization with the
+/// runtime log level.
#[no_mangle]
-pub extern "C" fn rs_log_set_level(level: i32) {
- log_set_level(level);
+pub unsafe extern "C" fn SCSetRustLogLevel(level: i32) {
+ LEVEL = level;
}
fn basename(filename: &str) -> &str {
return filename;
}
-pub fn sclog(level: Level, file: &str, line: u32, function: &str,
- message: &str)
-{
+pub fn sclog(level: Level, file: &str, line: u32, function: &str, message: &str) {
let filename = basename(file);
let noext = &filename[0..filename.len() - 3];
- sc_log_message(level,
- filename,
- line,
- function,
- noext,
- message);
+ sc_log_message(level, filename, line, function, noext, message);
+}
+
+/// SCLogMessage wrapper. If the Suricata C context is not registered
+/// a more basic log format will be used (for example, when running
+/// Rust unit tests).
+pub fn sc_log_message(
+ level: Level, filename: &str, line: std::os::raw::c_uint, function: &str, module: &str,
+ message: &str,
+) -> std::os::raw::c_int {
+ unsafe {
+ if let Some(c) = SC {
+ return (c.SCLogMessage)(
+ level as i32,
+ to_safe_cstring(filename).as_ptr(),
+ line,
+ to_safe_cstring(function).as_ptr(),
+ to_safe_cstring(module).as_ptr(),
+ to_safe_cstring(message).as_ptr(),
+ );
+ }
+ }
+
+ // Fall back if the Suricata C context is not registered which is
+ // the case when Rust unit tests are running.
+ //
+ // We don't log the time right now as I don't think it can be done
+ // with Rust 1.7.0 without using an external crate. With Rust
+ // 1.8.0 and newer we can unix UNIX_EPOCH.elapsed() to get the
+ // unix time.
+ println!("{}:{} <{:?}> -- {}", filename, line, level, message);
+ return 0;
+}
+
+// Convert a &str into a CString by first stripping NUL bytes.
+fn to_safe_cstring(val: &str) -> CString {
+ let mut safe = Vec::with_capacity(val.len());
+ for c in val.as_bytes() {
+ if *c != 0 {
+ safe.push(*c);
+ }
+ }
+ match CString::new(safe) {
+ Ok(cstr) => cstr,
+ _ => CString::new("<failed to encode string>").unwrap(),
+ }
}
// This macro returns the function name.
// is released under the MIT license as there is currently no macro in Rust
// to provide the function name.
#[macro_export(local_inner_macros)]
-macro_rules!function {
+macro_rules! function {
() => {{
- // Okay, this is ugly, I get it. However, this is the best we can get on a stable rust.
- fn __f() {}
- fn type_name_of<T>(_: T) -> &'static str {
- std::any::type_name::<T>()
- }
- let name = type_name_of(__f);
- &name[..name.len() - 5]
- }}
+ // Okay, this is ugly, I get it. However, this is the best we can get on a stable rust.
+ fn __f() {}
+ fn type_name_of<T>(_: T) -> &'static str {
+ std::any::type_name::<T>()
+ }
+ let name = type_name_of(__f);
+ &name[..name.len() - 5]
+ }};
}
#[macro_export]
macro_rules!do_log {
($level:expr, $($arg:tt)*) => {
- if $crate::log::get_log_level() >= $level as i32 {
- $crate::log::sclog($level, file!(), line!(), $crate::function!(),
+ #[allow(unused_unsafe)]
+ if unsafe { $crate::debug::LEVEL } >= $level as i32 {
+ $crate::debug::sclog($level, file!(), line!(), $crate::function!(),
&(format!($($arg)*)));
}
}
#[macro_export]
macro_rules!SCLogError {
($($arg:tt)*) => {
- $crate::do_log!($crate::log::Level::Error, $($arg)*);
+ $crate::do_log!($crate::debug::Level::Error, $($arg)*);
};
}
#[macro_export]
macro_rules!SCLogWarning {
($($arg:tt)*) => {
- $crate::do_log!($crate::log::Level::Warning, $($arg)*);
+ $crate::do_log!($crate::debug::Level::Warning, $($arg)*);
};
}
#[macro_export]
macro_rules!SCLogNotice {
($($arg:tt)*) => {
- $crate::do_log!($crate::log::Level::Notice, $($arg)*);
+ $crate::do_log!($crate::debug::Level::Notice, $($arg)*);
}
}
#[macro_export]
macro_rules!SCLogInfo {
($($arg:tt)*) => {
- $crate::do_log!($crate::log::Level::Info, $($arg)*);
+ $crate::do_log!($crate::debug::Level::Info, $($arg)*);
}
}
#[macro_export]
macro_rules!SCLogPerf {
($($arg:tt)*) => {
- $crate::do_log!($crate::log::Level::Perf, $($arg)*);
+ $crate::do_log!($crate::debug::Level::Perf, $($arg)*);
}
}
#[macro_export]
macro_rules!SCLogConfig {
($($arg:tt)*) => {
- $crate::do_log!($crate::log::Level::Config, $($arg)*);
+ $crate::do_log!($crate::debug::Level::Config, $($arg)*);
}
}
#[macro_export]
macro_rules!SCLogDebug {
($($arg:tt)*) => {
- do_log!($crate::log::Level::Debug, $($arg)*);
+ do_log!($crate::debug::Level::Debug, $($arg)*);
}
}
// about unused variables, but is otherwise the equivalent to a no-op.
#[cfg(not(feature = "debug"))]
#[macro_export]
-macro_rules!SCLogDebug {
- ($($arg:tt)*) => ()
+macro_rules! SCLogDebug {
+ ($($arg:tt)*) => {};
}
-/// SCLogMessage wrapper. If the Suricata C context is not registered
-/// a more basic log format will be used (for example, when running
-/// Rust unit tests).
-pub fn sc_log_message(level: Level,
- filename: &str,
- line: std::os::raw::c_uint,
- function: &str,
- module: &str,
- message: &str) -> std::os::raw::c_int
-{
- unsafe {
- if let Some(c) = SC {
- return (c.SCLogMessage)(
- level as i32,
- to_safe_cstring(filename).as_ptr(),
- line,
- to_safe_cstring(function).as_ptr(),
- to_safe_cstring(module).as_ptr(),
- to_safe_cstring(message).as_ptr());
- }
- }
+#[cfg(not(feature = "debug-validate"))]
+#[macro_export]
+macro_rules! debug_validate_bug_on (
+ ($item:expr) => {};
+);
- // Fall back if the Suricata C context is not registered which is
- // the case when Rust unit tests are running.
- //
- // We don't log the time right now as I don't think it can be done
- // with Rust 1.7.0 without using an external crate. With Rust
- // 1.8.0 and newer we can unix UNIX_EPOCH.elapsed() to get the
- // unix time.
- println!("{}:{} <{:?}> -- {}", filename, line, level, message);
- return 0;
-}
-// Convert a &str into a CString by first stripping NUL bytes.
-fn to_safe_cstring(val: &str) -> CString {
- let mut safe = Vec::with_capacity(val.len());
- for c in val.as_bytes() {
- if *c != 0 {
- safe.push(*c);
- }
+#[cfg(feature = "debug-validate")]
+#[macro_export]
+macro_rules! debug_validate_bug_on (
+ ($item:expr) => {
+ if $item {
+ panic!("Condition check failed");
}
- match CString::new(safe) {
- Ok(cstr) => cstr,
- _ => {
- CString::new("<failed to encode string>").unwrap()
- }
+ };
+);
+
+#[cfg(not(feature = "debug-validate"))]
+#[macro_export]
+macro_rules! debug_validate_fail (
+ ($msg:expr) => {};
+);
+
+#[cfg(feature = "debug-validate")]
+#[macro_export]
+macro_rules! debug_validate_fail (
+ ($msg:expr) => {
+ // Wrap in a conditional to prevent unreachable code warning in caller.
+ if true {
+ panic!($msg);
}
-}
+ };
+);