Skip to main content

falco_plugin/listen/
wrappers.rs

1use crate::base::wrappers::PluginWrapper;
2use crate::error::ffi_result::FfiResult;
3use crate::error::panic::catch_panic;
4use crate::listen::CaptureListenInput;
5use crate::listen::CaptureListenPlugin;
6use falco_plugin_api::{
7    plugin_api__bindgen_ty_5 as listen_plugin_api, ss_plugin_capture_listen_input, ss_plugin_rc,
8    ss_plugin_rc_SS_PLUGIN_FAILURE, ss_plugin_rc_SS_PLUGIN_SUCCESS, ss_plugin_t,
9};
10use std::panic::AssertUnwindSafe;
11
12/// Marker trait to mark a capture listen plugin as exported to the API
13///
14/// # Safety
15///
16/// Only implement this trait if you export the plugin either statically or dynamically
17/// to the plugin API. This is handled by the `capture_listen_plugin!` and `static_plugin!` macros, so you
18/// should never need to implement this trait manually.
19#[diagnostic::on_unimplemented(
20    message = "Capture listen plugin is not exported",
21    note = "use either `capture_listen_plugin!` or `static_plugin!`"
22)]
23pub unsafe trait CaptureListenPluginExported {}
24
25pub trait CaptureListenFallbackApi {
26    const LISTEN_API: listen_plugin_api = listen_plugin_api {
27        capture_open: None,
28        capture_close: None,
29    };
30
31    const IMPLEMENTS_LISTEN: bool = false;
32}
33
34impl<T> CaptureListenFallbackApi for T {}
35
36#[derive(Debug)]
37pub struct CaptureListenApi<T>(std::marker::PhantomData<T>);
38impl<T: CaptureListenPlugin + 'static> CaptureListenApi<T> {
39    pub const LISTEN_API: listen_plugin_api = listen_plugin_api {
40        capture_open: Some(plugin_capture_open::<T>),
41        capture_close: Some(plugin_capture_close::<T>),
42    };
43
44    pub const IMPLEMENTS_LISTEN: bool = true;
45}
46
47pub unsafe extern "C" fn plugin_capture_open<T: CaptureListenPlugin>(
48    plugin: *mut ss_plugin_t,
49    listen_input: *const ss_plugin_capture_listen_input,
50) -> ss_plugin_rc {
51    let plugin = unsafe {
52        let Some(plugin) = (plugin as *mut PluginWrapper<T>).as_mut() else {
53            return ss_plugin_rc_SS_PLUGIN_FAILURE;
54        };
55        plugin
56    };
57
58    let Some(actual_plugin) = &mut plugin.plugin else {
59        return ss_plugin_rc_SS_PLUGIN_FAILURE;
60    };
61
62    let listen_input = unsafe {
63        let Ok(listen_input) =
64            CaptureListenInput::try_from(listen_input, actual_plugin.last_error.clone())
65        else {
66            return ss_plugin_rc_SS_PLUGIN_FAILURE;
67        };
68        listen_input
69    };
70
71    if let Err(e) = catch_panic(AssertUnwindSafe(|| {
72        actual_plugin.plugin.capture_open(&listen_input)
73    })) {
74        e.set_last_error(&mut plugin.error_buf);
75        return e.status_code();
76    }
77
78    ss_plugin_rc_SS_PLUGIN_SUCCESS
79}
80
81pub unsafe extern "C" fn plugin_capture_close<T: CaptureListenPlugin>(
82    plugin: *mut ss_plugin_t,
83    listen_input: *const ss_plugin_capture_listen_input,
84) -> ss_plugin_rc {
85    let plugin = unsafe {
86        let Some(plugin) = (plugin as *mut PluginWrapper<T>).as_mut() else {
87            return ss_plugin_rc_SS_PLUGIN_FAILURE;
88        };
89        plugin
90    };
91
92    let Some(actual_plugin) = &mut plugin.plugin else {
93        return ss_plugin_rc_SS_PLUGIN_FAILURE;
94    };
95
96    let listen_input = unsafe {
97        let Ok(listen_input) =
98            CaptureListenInput::try_from(listen_input, actual_plugin.last_error.clone())
99        else {
100            return ss_plugin_rc_SS_PLUGIN_FAILURE;
101        };
102        listen_input
103    };
104
105    if let Err(e) = catch_panic(AssertUnwindSafe(|| {
106        actual_plugin.plugin.capture_close(&listen_input)
107    })) {
108        e.set_last_error(&mut plugin.error_buf);
109        return e.status_code();
110    }
111
112    ss_plugin_rc_SS_PLUGIN_SUCCESS
113}
114
115/// # Register an asynchronous event plugin
116///
117/// This macro must be called at most once in a crate (it generates public functions with fixed
118/// `#[unsafe(no_mangle)]` names) with a type implementing [`CaptureListenPlugin`] as the sole
119/// parameter.
120#[macro_export]
121macro_rules! capture_listen_plugin {
122    ($ty:ty) => {
123        unsafe impl $crate::listen::wrappers::CaptureListenPluginExported for $ty {}
124
125        $crate::wrap_ffi! {
126            #[unsafe(no_mangle)]
127            use $crate::listen::wrappers: <$ty>;
128
129            unsafe fn plugin_capture_open(
130                plugin: *mut falco_plugin::api::ss_plugin_t,
131                listen_input: *const falco_plugin::api::ss_plugin_capture_listen_input,
132            ) -> falco_plugin::api::ss_plugin_rc;
133            unsafe fn plugin_capture_close(
134                plugin: *mut falco_plugin::api::ss_plugin_t,
135                listen_input: *const falco_plugin::api::ss_plugin_capture_listen_input,
136            ) -> falco_plugin::api::ss_plugin_rc;
137        }
138
139        #[allow(dead_code)]
140        fn __typecheck_plugin_listen_api() -> falco_plugin::api::plugin_api__bindgen_ty_5 {
141            falco_plugin::api::plugin_api__bindgen_ty_5 {
142                capture_open: Some(plugin_capture_open),
143                capture_close: Some(plugin_capture_close),
144            }
145        }
146    };
147}