From b4db7bdaee29f4384f8f9389df0a6ca0d5c4a443 Mon Sep 17 00:00:00 2001 From: Jason Ish Date: Wed, 4 Feb 2026 11:28:44 -0600 Subject: [PATCH] rust/ffi: add logging (debug) wrappers Mostly a copy of Suricata core's logging wrappers into the ffi crate. These are not yet used by Suricata-core as they do require the Suricata library to be available, which is not the case with tests. And the `cfg(test)` parameter is not passed through to sub-crates. However, this does allow a plugin (or library) to call the logging macros without depending on the "suricata" crate. Ticket: #7666 --- rust/Cargo.lock.in | 3 + rust/ffi/Cargo.toml.in | 6 ++ rust/ffi/src/debug.rs | 193 +++++++++++++++++++++++++++++++++++++++++ rust/ffi/src/lib.rs | 3 + rust/ffi/src/plugin.rs | 26 ++++++ 5 files changed, 231 insertions(+) create mode 100644 rust/ffi/src/debug.rs create mode 100644 rust/ffi/src/plugin.rs diff --git a/rust/Cargo.lock.in b/rust/Cargo.lock.in index 283c1aaeae..733a8e4284 100644 --- a/rust/Cargo.lock.in +++ b/rust/Cargo.lock.in @@ -1606,6 +1606,9 @@ dependencies = [ [[package]] name = "suricata-ffi" version = "9.0.0-dev" +dependencies = [ + "suricata-sys", +] [[package]] name = "suricata-htp" diff --git a/rust/ffi/Cargo.toml.in b/rust/ffi/Cargo.toml.in index a9d66d3282..f8f0d6be96 100644 --- a/rust/ffi/Cargo.toml.in +++ b/rust/ffi/Cargo.toml.in @@ -4,3 +4,9 @@ version = "@PACKAGE_VERSION@" edition = "2021" license = "GPL-2.0-only" description = "Nice wrappers around Suricata C interface" + +[dependencies] +suricata-sys = { path = "../sys" } + +[features] +debug = [] diff --git a/rust/ffi/src/debug.rs b/rust/ffi/src/debug.rs new file mode 100644 index 0000000000..7c0ffcff48 --- /dev/null +++ b/rust/ffi/src/debug.rs @@ -0,0 +1,193 @@ +/* Copyright (C) 2017-2026 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 + * Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * version 2 along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +//! Logging and debug utilities, like util-debug.c. + +use std::{ffi::CString, path::Path}; + +use suricata_sys::sys::{SCError, SCLogMessage}; +use suricata_sys::sys::{SCFatalErrorOnInitStatic, SCLogLevel}; + +/// The Suricata log-level. +/// +/// This must be set on start-up of Suricata, as well as on plugin +/// initialization so the plugin inherits the same log-level as the +/// running Suricata. +pub static mut LEVEL: SCLogLevel = SCLogLevel::SC_LOG_NOTSET; + +/// Set the Rust context's idea of the log level. +/// +/// This will be called during Suricata initialization with the +/// runtime log level. +/// +/// # Safety +/// +/// This function writes to the global variable LEVEL. This variable +/// should not be read or written from multiple threads, so this is +/// best called before we spawn any threads. +pub unsafe fn set_log_level(level: SCLogLevel) { + LEVEL = level; +} + +fn basename(filename: &str) -> &str { + let path = Path::new(filename); + if let Some(os_str) = path.file_name() { + if let Some(basename) = os_str.to_str() { + return basename; + } + } + filename +} + +pub fn fatalerror(message: &str) { + unsafe { + SCFatalErrorOnInitStatic(to_safe_cstring(message).as_ptr()); + } +} + +pub fn sclog(level: SCLogLevel, file: &str, line: u32, function: &str, message: &str) { + let filename = basename(file); + let noext = &filename[0..filename.len() - 3]; + log_message(level, filename, line, function, noext, message); +} + +/// SCLogMessage wrapper. +pub fn log_message( + level: SCLogLevel, filename: &str, line: std::os::raw::c_uint, function: &str, module: &str, + message: &str, +) -> SCError { + unsafe { + SCLogMessage( + level, + 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(), + ) + } +} + +// 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("").unwrap(), + } +} + +// This macro returns the function name. +// +// This macro has been borrowed from https://github.com/popzxc/stdext-rs, which +// 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 { + () => {{ + // 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) -> &'static str { + std::any::type_name::() + } + let name = type_name_of(__f); + &name[..name.len() - 5] + }}; +} + +#[macro_export] +macro_rules! do_log { + ($level:expr, $($arg:tt)*) => { + #[allow(unused_unsafe)] + if unsafe { $crate::debug::LEVEL as i32 } >= $level as i32 { + $crate::debug::sclog($level, file!(), line!(), $crate::function!(), + &(format!($($arg)*))); + } + } +} + +#[macro_export] +macro_rules! SCLogError { + ($($arg:tt)*) => { + $crate::do_log!(suricata_sys::sys::SCLogLevel::SC_LOG_ERROR, $($arg)*); + }; +} + +#[macro_export] +macro_rules! SCLogWarning { + ($($arg:tt)*) => { + $crate::do_log!(suricata_sys::sys::SCLogLevel::SC_LOG_WARNING, $($arg)*); + }; +} + +#[macro_export] +macro_rules! SCLogNotice { + ($($arg:tt)*) => { + $crate::do_log!(suricata_sys::sys::SCLogLevel::SC_LOG_NOTICE, $($arg)*); + } +} + +#[macro_export] +macro_rules! SCLogInfo { + ($($arg:tt)*) => { + $crate::do_log!(suricata_sys::sys::SCLogLevel::SC_LOG_INFO, $($arg)*); + } +} + +#[macro_export] +macro_rules! SCLogPerf { + ($($arg:tt)*) => { + $crate::do_log!(suricata_sys::sys::SCLogLevel::SC_LOG_PERF, $($arg)*); + } +} + +#[macro_export] +macro_rules! SCLogConfig { + ($($arg:tt)*) => { + $crate::do_log!(suricata_sys::sys::SCLogLevel::SC_LOG_CONFIG, $($arg)*); + } +} + +// Debug mode: call C SCLogDebug +#[cfg(feature = "debug")] +#[macro_export] +macro_rules!SCLogDebug { + ($($arg:tt)*) => { + $crate::do_log!(suricata_sys::sys::SCLogLevel::SC_LOG_DEBUG, $($arg)*); + } +} + +// SCLogDebug variation to use when not compiled with debug +// support. This just swallows the arguments and doesn't output +// anything. +#[cfg(not(feature = "debug"))] +#[macro_export] +macro_rules! SCLogDebug { + ($($arg:tt)*) => {}; +} + +#[macro_export] +macro_rules! SCFatalErrorOnInit { + ($($arg:tt)*) => { + $crate::debug::fatalerror(&format!($($arg)*)); + } +} diff --git a/rust/ffi/src/lib.rs b/rust/ffi/src/lib.rs index fb5ac17780..0f16d8b54f 100644 --- a/rust/ffi/src/lib.rs +++ b/rust/ffi/src/lib.rs @@ -14,3 +14,6 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. */ + +pub mod debug; +pub mod plugin; diff --git a/rust/ffi/src/plugin.rs b/rust/ffi/src/plugin.rs new file mode 100644 index 0000000000..f236cde551 --- /dev/null +++ b/rust/ffi/src/plugin.rs @@ -0,0 +1,26 @@ +/* Copyright (C) 2026 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 + * Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * version 2 along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +//! Plugin utility module. + +use suricata_sys::sys::SCLogGetLogLevel; + +pub fn init() { + unsafe { + crate::debug::set_log_level(SCLogGetLogLevel()); + } +}