falco_plugin/plugin/parse/
wrappers.rs

1use crate::parse::EventInput;
2use crate::plugin::base::PluginWrapper;
3use crate::plugin::error::ffi_result::FfiResult;
4use crate::plugin::parse::{ParseInput, ParsePlugin};
5use falco_event::events::AnyEventPayload;
6use falco_plugin_api::plugin_api__bindgen_ty_3 as parse_plugin_api;
7use falco_plugin_api::{
8    ss_plugin_event_input, ss_plugin_event_parse_input, ss_plugin_rc,
9    ss_plugin_rc_SS_PLUGIN_FAILURE, ss_plugin_t,
10};
11use std::any::TypeId;
12use std::collections::BTreeMap;
13use std::ffi::{c_char, CString};
14use std::marker::PhantomData;
15use std::sync::Mutex;
16
17/// Marker trait to mark a parse plugin as exported to the API
18///
19/// # Safety
20///
21/// Only implement this trait if you export the plugin either statically or dynamically
22/// to the plugin API. This is handled by the `parse_plugin!` and `static_plugin!` macros, so you
23/// should never need to implement this trait manually.
24#[diagnostic::on_unimplemented(
25    message = "Parse plugin is not exported",
26    note = "use either `parse_plugin!` or `static_plugin!`"
27)]
28pub unsafe trait ParsePluginExported {}
29
30pub trait ParsePluginFallbackApi {
31    const PARSE_API: parse_plugin_api = parse_plugin_api {
32        get_parse_event_types: None,
33        get_parse_event_sources: None,
34        parse_event: None,
35    };
36
37    const IMPLEMENTS_PARSE: bool = false;
38}
39impl<T> ParsePluginFallbackApi for T {}
40
41#[allow(missing_debug_implementations)]
42pub struct ParsePluginApi<T>(std::marker::PhantomData<T>);
43
44impl<T: ParsePlugin + 'static> ParsePluginApi<T> {
45    pub const PARSE_API: parse_plugin_api = parse_plugin_api {
46        get_parse_event_types: Some(plugin_get_parse_event_types::<T>),
47        get_parse_event_sources: Some(plugin_get_parse_event_sources::<T>),
48        parse_event: Some(plugin_parse_event::<T>),
49    };
50
51    pub const IMPLEMENTS_PARSE: bool = true;
52}
53
54/// # Safety
55///
56/// All pointers must be valid
57pub unsafe extern "C-unwind" fn plugin_get_parse_event_types<T: ParsePlugin>(
58    numtypes: *mut u32,
59    _plugin: *mut ss_plugin_t,
60) -> *mut u16 {
61    let types = T::Event::EVENT_TYPES;
62    if let Some(numtypes) = unsafe { numtypes.as_mut() } {
63        *numtypes = types.len() as u32;
64        types.as_ptr().cast_mut() // this should ****really**** be const
65    } else {
66        std::ptr::null_mut()
67    }
68}
69
70//noinspection DuplicatedCode
71pub extern "C-unwind" fn plugin_get_parse_event_sources<T: ParsePlugin + 'static>() -> *const c_char
72{
73    static SOURCES: Mutex<BTreeMap<TypeId, CString>> = Mutex::new(BTreeMap::new());
74
75    let ty = TypeId::of::<T>();
76    let mut sources_map = SOURCES.lock().unwrap();
77    // we only generate the string once and never change or delete it
78    // so the pointer should remain valid for the static lifetime
79    sources_map
80        .entry(ty)
81        .or_insert_with(|| {
82            let sources = serde_json::to_string(T::Event::event_sources().as_slice())
83                .expect("failed to serialize event source array");
84            CString::new(sources.into_bytes()).expect("failed to add NUL to event source array")
85        })
86        .as_ptr()
87}
88
89/// # Safety
90///
91/// All pointers must be valid
92pub unsafe extern "C-unwind" fn plugin_parse_event<T: ParsePlugin>(
93    plugin: *mut ss_plugin_t,
94    event: *const ss_plugin_event_input,
95    parse_input: *const ss_plugin_event_parse_input,
96) -> ss_plugin_rc {
97    let plugin = plugin as *mut PluginWrapper<T>;
98    unsafe {
99        let Some(plugin) = plugin.as_mut() else {
100            return ss_plugin_rc_SS_PLUGIN_FAILURE;
101        };
102        let Some(actual_plugin) = &mut plugin.plugin else {
103            return ss_plugin_rc_SS_PLUGIN_FAILURE;
104        };
105
106        let Some(event) = event.as_ref() else {
107            return ss_plugin_rc_SS_PLUGIN_FAILURE;
108        };
109        let event = EventInput(*event, PhantomData);
110
111        let Ok(parse_input) = ParseInput::try_from(parse_input, actual_plugin.last_error.clone())
112        else {
113            return ss_plugin_rc_SS_PLUGIN_FAILURE;
114        };
115
116        actual_plugin
117            .plugin
118            .parse_event(&event, &parse_input)
119            .rc(&mut plugin.error_buf)
120    }
121}
122
123/// # Register an event parsing plugin
124///
125/// This macro must be called at most once in a crate (it generates public functions with fixed
126/// `#[unsafe(no_mangle)]` names) with a type implementing [`ParsePlugin`] as the sole parameter.
127#[macro_export]
128macro_rules! parse_plugin {
129    ($ty:ty) => {
130        unsafe impl $crate::internals::parse::wrappers::ParsePluginExported for $ty {}
131
132        $crate::wrap_ffi! {
133            #[unsafe(no_mangle)]
134            use $crate::internals::parse::wrappers: <$ty>;
135
136            unsafe fn plugin_get_parse_event_types(
137                numtypes: *mut u32,
138                plugin: *mut falco_plugin::api::ss_plugin_t,
139            ) -> *mut u16;
140            unsafe fn plugin_get_parse_event_sources() -> *const ::std::ffi::c_char;
141            unsafe fn plugin_parse_event(
142                plugin: *mut falco_plugin::api::ss_plugin_t,
143                event_input: *const falco_plugin::api::ss_plugin_event_input,
144                parse_input: *const falco_plugin::api::ss_plugin_event_parse_input,
145            ) -> i32;
146        }
147
148        #[allow(dead_code)]
149        fn __typecheck_plugin_parse_api() -> falco_plugin::api::plugin_api__bindgen_ty_3 {
150            falco_plugin::api::plugin_api__bindgen_ty_3 {
151                get_parse_event_types: Some(plugin_get_parse_event_types),
152                get_parse_event_sources: Some(plugin_get_parse_event_sources),
153                parse_event: Some(plugin_parse_event),
154            }
155        }
156    };
157}