Skip to main content

falco_plugin/async_event/
wrappers.rs

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