From ea3a4e4eafad1e8a28ea127ca964f0c32cde9214 Mon Sep 17 00:00:00 2001 From: Jason Ish Date: Wed, 27 May 2026 14:40:58 -0600 Subject: [PATCH] rust/ffi: add thread lifecycle callback wrappers Ticket: #8605 --- examples/plugins/rust/src/mod.rs | 16 +++++++++ rust/ffi/src/lib.rs | 1 + rust/ffi/src/thread.rs | 56 ++++++++++++++++++++++++++++++++ 3 files changed, 73 insertions(+) create mode 100644 rust/ffi/src/thread.rs diff --git a/examples/plugins/rust/src/mod.rs b/examples/plugins/rust/src/mod.rs index 4be19cca50..2ce8d78a28 100644 --- a/examples/plugins/rust/src/mod.rs +++ b/examples/plugins/rust/src/mod.rs @@ -4,6 +4,7 @@ use suricata_ffi::eve::{self, SCJsonBuilder}; use suricata_ffi::flow::{self, Flow, FlowStorage}; use suricata_ffi::jsonbuilder::JsonBuilder; use suricata_ffi::packet::Packet; +use suricata_ffi::thread; use suricata_ffi::threadvars::ThreadVars; use suricata_ffi::{SCLogError, SCLogNotice}; use suricata_sys::sys::{ @@ -23,6 +24,7 @@ unsafe extern "C" fn init() { pub fn register() -> Result<(), &'static str> { register_eve_callbacks()?; register_flow_callbacks()?; + register_thread_callbacks()?; Ok(()) } @@ -33,6 +35,13 @@ pub fn register_eve_callbacks() -> Result<(), &'static str> { eve::register_callback(log_eve_wrapped) } +pub fn register_thread_callbacks() -> Result<(), &'static str> { + // This thread init callback registration shows how we can use a closure to + // pass "user" data. + let user_data = "foo"; + thread::register_init_callback(|tv| on_thread_init(tv, user_data)) +} + #[derive(Default)] struct ExampleFlowState { packets: u64, @@ -74,6 +83,13 @@ fn log_eve_wrapped( Ok(()) } +fn on_thread_init(tv: ThreadVars<'_>, _foo: &str) { + SCLogNotice!( + "rust example thread init callback: thread={:p}", + tv.as_ptr() + ); +} + fn log_flow_init( _tv: ThreadVars<'_>, mut f: Flow<'_>, diff --git a/rust/ffi/src/lib.rs b/rust/ffi/src/lib.rs index 7a33005646..1b7e77d3f8 100644 --- a/rust/ffi/src/lib.rs +++ b/rust/ffi/src/lib.rs @@ -25,6 +25,7 @@ pub mod flow; pub mod jsonbuilder; pub mod packet; pub mod plugin; +pub mod thread; pub mod threadvars; pub const IPPROTO_TCP: u8 = 6; diff --git a/rust/ffi/src/thread.rs b/rust/ffi/src/thread.rs new file mode 100644 index 0000000000..f689b137bc --- /dev/null +++ b/rust/ffi/src/thread.rs @@ -0,0 +1,56 @@ +/* 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. + */ + +use std::os::raw::c_void; + +use crate::threadvars::ThreadVars; +use suricata_sys::sys; + +/// Register a thread initialization callback. +/// +/// The callback is invoked for every thread being initialized during Suricata +/// startup. It receives the `ThreadVars` for the thread that has just been +/// initialized. +/// +/// # Safety +/// +/// The callback receives raw pointers from Suricata. These pointers are only +/// valid for the duration of the callback invocation and must not be stored. +/// +/// The callback must not panic. +pub fn register_init_callback(callback: F) -> Result<(), &'static str> +where + F: for<'a> Fn(ThreadVars<'a>) + Send + Sync + 'static, +{ + let user = Box::into_raw(Box::new(callback)) as *mut c_void; + if unsafe { sys::SCThreadRegisterInitCallback(Some(init_callback_wrapper::), user) } { + Ok(()) + } else { + unsafe { + drop(Box::from_raw(user as *mut F)); + } + Err("Failed to register thread init callback") + } +} + +unsafe extern "C" fn init_callback_wrapper(tv: *mut sys::ThreadVars, user: *mut c_void) +where + F: for<'a> Fn(ThreadVars<'a>) + Send + Sync + 'static, +{ + let callback = &*(user as *const F); + callback(ThreadVars::from_ptr(tv)); +}