falco_plugin/plugin/async_event/
wrappers.rs

1use crate::plugin::async_event::async_handler::AsyncHandler;
2use crate::plugin::async_event::AsyncEventPlugin;
3use crate::plugin::base::PluginWrapper;
4use crate::plugin::error::ffi_result::FfiResult;
5use falco_plugin_api::plugin_api__bindgen_ty_4 as async_plugin_api;
6use falco_plugin_api::{
7    ss_plugin_async_event_handler_t, ss_plugin_owner_t, ss_plugin_rc,
8    ss_plugin_rc_SS_PLUGIN_FAILURE, ss_plugin_rc_SS_PLUGIN_SUCCESS, ss_plugin_t,
9};
10use std::any::TypeId;
11use std::collections::BTreeMap;
12use std::ffi::{c_char, CString};
13use std::sync::Mutex;
14
15/// Marker trait to mark an async plugin as exported to the API
16///
17/// # Safety
18///
19/// Only implement this trait if you export the plugin either statically or dynamically
20/// to the plugin API. This is handled by the `async_plugin!` and `static_plugin!` macros, so you
21/// should never need to implement this trait manually.
22#[diagnostic::on_unimplemented(
23    message = "Async plugin is not exported",
24    note = "use either `async_plugin!` or `static_plugin!`"
25)]
26pub unsafe trait AsyncPluginExported {}
27
28pub trait AsyncPluginFallbackApi {
29    const ASYNC_API: async_plugin_api = async_plugin_api {
30        get_async_event_sources: None,
31        get_async_events: None,
32        set_async_event_handler: None,
33        dump_state: None,
34    };
35
36    const IMPLEMENTS_ASYNC: bool = false;
37}
38impl<T> AsyncPluginFallbackApi for T {}
39
40#[derive(Debug)]
41pub struct AsyncPluginApi<T>(std::marker::PhantomData<T>);
42impl<T: AsyncEventPlugin + 'static> AsyncPluginApi<T> {
43    pub const ASYNC_API: async_plugin_api = async_plugin_api {
44        get_async_event_sources: Some(plugin_get_async_event_sources::<T>),
45        get_async_events: Some(plugin_get_async_events::<T>),
46        set_async_event_handler: Some(plugin_set_async_event_handler::<T>),
47        dump_state: Some(plugin_dump_state::<T>),
48    };
49
50    pub const IMPLEMENTS_ASYNC: bool = true;
51}
52
53pub extern "C-unwind" fn plugin_get_async_event_sources<T: AsyncEventPlugin + 'static>(
54) -> *const c_char {
55    static SOURCES: Mutex<BTreeMap<TypeId, CString>> = Mutex::new(BTreeMap::new());
56
57    let ty = TypeId::of::<T>();
58    let mut sources_map = SOURCES.lock().unwrap();
59    // we only generate the string once and never change or delete it
60    // so the pointer should remain valid for the static lifetime
61    sources_map
62        .entry(ty)
63        .or_insert_with(|| {
64            let sources = serde_json::to_string(T::EVENT_SOURCES)
65                .expect("failed to serialize event source array");
66            CString::new(sources.into_bytes()).expect("failed to add NUL to event source array")
67        })
68        .as_ptr()
69}
70
71pub extern "C-unwind" fn plugin_get_async_events<T: AsyncEventPlugin + 'static>() -> *const c_char {
72    static EVENTS: Mutex<BTreeMap<TypeId, CString>> = Mutex::new(BTreeMap::new());
73
74    let ty = TypeId::of::<T>();
75    let mut event_map = EVENTS.lock().unwrap();
76    // we only generate the string once and never change or delete it
77    // so the pointer should remain valid for the static lifetime
78    event_map
79        .entry(ty)
80        .or_insert_with(|| {
81            let sources = serde_json::to_string(T::ASYNC_EVENTS)
82                .expect("failed to serialize event name array");
83            CString::new(sources.into_bytes()).expect("failed to add NUL to event name array")
84        })
85        .as_ptr()
86}
87
88/// # Safety
89///
90/// All pointers must be valid
91pub unsafe extern "C-unwind" fn plugin_set_async_event_handler<T: AsyncEventPlugin>(
92    plugin: *mut ss_plugin_t,
93    owner: *mut ss_plugin_owner_t,
94    handler: ss_plugin_async_event_handler_t,
95) -> ss_plugin_rc {
96    unsafe {
97        let Some(plugin) = (plugin as *mut PluginWrapper<T>).as_mut() else {
98            return ss_plugin_rc_SS_PLUGIN_FAILURE;
99        };
100
101        let Some(actual_plugin) = &mut plugin.plugin else {
102            return ss_plugin_rc_SS_PLUGIN_FAILURE;
103        };
104
105        if let Err(e) = actual_plugin.plugin.stop_async() {
106            e.set_last_error(&mut plugin.error_buf);
107            return e.status_code();
108        }
109
110        let Some(raw_handler) = handler.as_ref() else {
111            return ss_plugin_rc_SS_PLUGIN_SUCCESS;
112        };
113
114        let handler = AsyncHandler {
115            owner,
116            raw_handler: *raw_handler,
117        };
118        if let Err(e) = actual_plugin.plugin.start_async(handler) {
119            e.set_last_error(&mut plugin.error_buf);
120            return e.status_code();
121        }
122
123        ss_plugin_rc_SS_PLUGIN_SUCCESS
124    }
125}
126
127/// # Safety
128///
129/// All pointers must be valid
130pub unsafe extern "C-unwind" fn plugin_dump_state<T: AsyncEventPlugin>(
131    plugin: *mut ss_plugin_t,
132    owner: *mut ss_plugin_owner_t,
133    handler: ss_plugin_async_event_handler_t,
134) -> ss_plugin_rc {
135    unsafe {
136        let Some(plugin) = (plugin as *mut PluginWrapper<T>).as_mut() else {
137            return ss_plugin_rc_SS_PLUGIN_FAILURE;
138        };
139
140        let Some(actual_plugin) = &mut plugin.plugin else {
141            return ss_plugin_rc_SS_PLUGIN_FAILURE;
142        };
143
144        let Some(raw_handler) = handler.as_ref() else {
145            return ss_plugin_rc_SS_PLUGIN_SUCCESS;
146        };
147
148        let handler = AsyncHandler {
149            owner,
150            raw_handler: *raw_handler,
151        };
152        if let Err(e) = actual_plugin.plugin.dump_state(handler) {
153            e.set_last_error(&mut plugin.error_buf);
154            return e.status_code();
155        }
156
157        ss_plugin_rc_SS_PLUGIN_SUCCESS
158    }
159}
160
161/// # Register an asynchronous event plugin
162///
163/// This macro must be called at most once in a crate (it generates public functions with fixed
164/// `#[unsafe(no_mangle)]` names) with a type implementing [`AsyncEventPlugin`] as the sole
165/// parameter.
166#[macro_export]
167macro_rules! async_event_plugin {
168    ($ty:ty) => {
169        unsafe impl $crate::internals::async_event::wrappers::AsyncPluginExported for $ty {}
170
171        $crate::wrap_ffi! {
172            #[unsafe(no_mangle)]
173            use $crate::internals::async_event::wrappers: <$ty>;
174
175            unsafe fn plugin_get_async_events() -> *const ::std::ffi::c_char;
176            unsafe fn plugin_get_async_event_sources() -> *const ::std::ffi::c_char;
177            unsafe fn plugin_set_async_event_handler(
178                plugin: *mut falco_plugin::api::ss_plugin_t,
179                owner: *mut falco_plugin::api::ss_plugin_owner_t,
180                handler: falco_plugin::api::ss_plugin_async_event_handler_t,
181            ) -> falco_plugin::api::ss_plugin_rc;
182            unsafe fn plugin_dump_state(
183                plugin: *mut falco_plugin::api::ss_plugin_t,
184                owner: *mut falco_plugin::api::ss_plugin_owner_t,
185                handler: falco_plugin::api::ss_plugin_async_event_handler_t,
186            ) -> falco_plugin::api::ss_plugin_rc;
187        }
188
189        #[allow(dead_code)]
190        fn __typecheck_plugin_async_api() -> falco_plugin::api::plugin_api__bindgen_ty_4 {
191            falco_plugin::api::plugin_api__bindgen_ty_4 {
192                get_async_event_sources: Some(plugin_get_async_event_sources),
193                get_async_events: Some(plugin_get_async_events),
194                set_async_event_handler: Some(plugin_set_async_event_handler),
195                dump_state: Some(plugin_dump_state),
196            }
197        }
198    };
199}