falco_plugin/plugin/source/
wrappers.rs

1use crate::plugin::base::PluginWrapper;
2use crate::plugin::error::ffi_result::FfiResult;
3use crate::plugin::source::SourcePluginInstanceWrapper;
4use crate::source::{EventBatch, EventInput, SourcePlugin, SourcePluginInstance};
5use crate::strings::cstring_writer::WriteIntoCString;
6use crate::strings::from_ptr::try_str_from_ptr;
7use falco_plugin_api::plugin_api__bindgen_ty_1 as source_plugin_api;
8use falco_plugin_api::{
9    ss_instance_t, ss_plugin_event, ss_plugin_event_input, ss_plugin_rc,
10    ss_plugin_rc_SS_PLUGIN_FAILURE, ss_plugin_rc_SS_PLUGIN_SUCCESS, ss_plugin_t,
11};
12use std::ffi::c_char;
13use std::io::Write;
14use std::marker::PhantomData;
15
16/// Marker trait to mark a source plugin as exported to the API
17///
18/// # Safety
19///
20/// Only implement this trait if you export the plugin either statically or dynamically
21/// to the plugin API. This is handled by the `source_plugin!` and `static_plugin!` macros, so you
22/// should never need to implement this trait manually.
23#[diagnostic::on_unimplemented(
24    message = "Source plugin is not exported",
25    note = "use either `source_plugin!` or `static_plugin!`"
26)]
27pub unsafe trait SourcePluginExported {}
28
29pub trait SourcePluginFallbackApi {
30    const SOURCE_API: source_plugin_api = source_plugin_api {
31        get_id: None,
32        get_event_source: None,
33        open: None,
34        close: None,
35        list_open_params: None,
36        get_progress: None,
37        event_to_string: None,
38        next_batch: None,
39    };
40
41    const IMPLEMENTS_SOURCE: bool = false;
42}
43impl<T> SourcePluginFallbackApi for T {}
44
45#[allow(missing_debug_implementations)]
46pub struct SourcePluginApi<T>(std::marker::PhantomData<T>);
47
48const fn impl_source_plugin_api<T: SourcePlugin>() -> source_plugin_api {
49    if T::PLUGIN_ID != 0 && T::EVENT_SOURCE.is_empty() {
50        panic!("EVENT_SOURCE cannot be empty if PLUGIN_ID is non-zero")
51    }
52
53    source_plugin_api {
54        get_id: Some(plugin_get_id::<T>),
55        get_event_source: Some(plugin_get_event_source::<T>),
56        open: Some(plugin_open::<T>),
57        close: Some(plugin_close::<T>),
58        list_open_params: Some(plugin_list_open_params::<T>),
59        get_progress: Some(plugin_get_progress::<T>),
60        event_to_string: Some(plugin_event_to_string::<T>),
61        next_batch: Some(plugin_next_batch::<T>),
62    }
63}
64
65impl<T: SourcePlugin> SourcePluginApi<T> {
66    pub const SOURCE_API: source_plugin_api = impl_source_plugin_api::<T>();
67
68    pub const IMPLEMENTS_SOURCE: bool = true;
69}
70
71pub extern "C-unwind" fn plugin_get_event_source<T: SourcePlugin>() -> *const c_char {
72    T::EVENT_SOURCE.as_ptr()
73}
74
75pub extern "C-unwind" fn plugin_get_id<T: SourcePlugin>() -> u32 {
76    T::PLUGIN_ID
77}
78
79/// # Safety
80///
81/// All pointers must be valid
82pub unsafe extern "C-unwind" fn plugin_list_open_params<T: SourcePlugin>(
83    plugin: *mut ss_plugin_t,
84    rc: *mut i32,
85) -> *const c_char {
86    let plugin = plugin as *mut PluginWrapper<T>;
87    let plugin = unsafe {
88        let Some(plugin) = plugin.as_mut() else {
89            return std::ptr::null();
90        };
91        plugin
92    };
93    let Some(actual_plugin) = &mut plugin.plugin else {
94        return std::ptr::null();
95    };
96
97    match actual_plugin.plugin.list_open_params() {
98        Ok(s) => {
99            unsafe {
100                *rc = ss_plugin_rc_SS_PLUGIN_SUCCESS;
101            }
102            s.as_ptr()
103        }
104        Err(e) => {
105            unsafe {
106                *rc = e.status_code();
107            }
108            e.set_last_error(&mut plugin.error_buf);
109            std::ptr::null()
110        }
111    }
112}
113
114/// # Safety
115///
116/// All pointers must be valid
117pub unsafe extern "C-unwind" fn plugin_open<T: SourcePlugin>(
118    plugin: *mut ss_plugin_t,
119    params: *const c_char,
120    rc: *mut ss_plugin_rc,
121) -> *mut ss_instance_t {
122    let plugin = plugin as *mut PluginWrapper<T>;
123    unsafe {
124        let Some(plugin) = plugin.as_mut() else {
125            return std::ptr::null_mut();
126        };
127        let Some(actual_plugin) = &mut plugin.plugin else {
128            return std::ptr::null_mut();
129        };
130
131        let Some(rc) = rc.as_mut() else {
132            return std::ptr::null_mut();
133        };
134
135        let params = if params.is_null() {
136            None
137        } else {
138            match try_str_from_ptr(&params) {
139                Ok(params) => Some(params),
140                Err(e) => {
141                    plugin
142                        .error_buf
143                        .write_into(|w| w.write_all(e.to_string().as_bytes()))
144                        .ok();
145                    *rc = ss_plugin_rc_SS_PLUGIN_FAILURE;
146
147                    return std::ptr::null_mut();
148                }
149            }
150        };
151
152        match actual_plugin.plugin.open(params) {
153            Ok(instance) => {
154                *rc = ss_plugin_rc_SS_PLUGIN_SUCCESS;
155                Box::into_raw(Box::new(SourcePluginInstanceWrapper {
156                    instance,
157                    batch: Default::default(),
158                }))
159                .cast()
160            }
161            Err(e) => {
162                e.set_last_error(&mut plugin.error_buf);
163                *rc = e.status_code();
164                std::ptr::null_mut()
165            }
166        }
167    }
168}
169
170/// # Safety
171///
172/// All pointers must be valid
173pub unsafe extern "C-unwind" fn plugin_close<T: SourcePlugin>(
174    plugin: *mut ss_plugin_t,
175    instance: *mut ss_instance_t,
176) {
177    let plugin = plugin as *mut PluginWrapper<T>;
178    let plugin = unsafe {
179        let Some(plugin) = plugin.as_mut() else {
180            return;
181        };
182        plugin
183    };
184    let Some(actual_plugin) = &mut plugin.plugin else {
185        return;
186    };
187
188    let instance = instance as *mut SourcePluginInstanceWrapper<T::Instance>;
189    if instance.is_null() {
190        return;
191    }
192    unsafe {
193        let mut inst = Box::from_raw(instance);
194        actual_plugin.plugin.close(&mut inst.instance);
195    }
196}
197
198/// # Safety
199///
200/// All pointers must be valid
201pub unsafe extern "C-unwind" fn plugin_next_batch<T: SourcePlugin>(
202    plugin: *mut ss_plugin_t,
203    instance: *mut ss_instance_t,
204    nevts: *mut u32,
205    evts: *mut *mut *mut ss_plugin_event,
206) -> ss_plugin_rc {
207    let plugin = plugin as *mut PluginWrapper<T>;
208    let instance = instance as *mut SourcePluginInstanceWrapper<T::Instance>;
209    unsafe {
210        let Some(plugin) = plugin.as_mut() else {
211            return ss_plugin_rc_SS_PLUGIN_FAILURE;
212        };
213        let Some(actual_plugin) = &mut plugin.plugin else {
214            return ss_plugin_rc_SS_PLUGIN_FAILURE;
215        };
216
217        let Some(instance) = instance.as_mut() else {
218            return ss_plugin_rc_SS_PLUGIN_FAILURE;
219        };
220
221        instance.batch.reset();
222        let mut batch = EventBatch::new(&instance.batch);
223        let batch_result = instance
224            .instance
225            .next_batch(&mut actual_plugin.plugin, &mut batch);
226        match batch_result {
227            Ok(()) => {
228                let events = batch.get_events();
229                *nevts = events.len() as u32;
230                *evts = events as *const _ as *mut _;
231                ss_plugin_rc_SS_PLUGIN_SUCCESS
232            }
233            Err(e) => {
234                *nevts = 0;
235                *evts = std::ptr::null_mut();
236                e.set_last_error(&mut plugin.error_buf);
237                e.status_code()
238            }
239        }
240    }
241}
242
243/// # Safety
244///
245/// All pointers must be valid
246pub unsafe extern "C-unwind" fn plugin_get_progress<T: SourcePlugin>(
247    _plugin: *mut ss_plugin_t,
248    instance: *mut ss_instance_t,
249    progress_pct: *mut u32,
250) -> *const c_char {
251    let instance = instance as *mut SourcePluginInstanceWrapper<T::Instance>;
252    let progress = unsafe { instance.as_mut() }.map(|instance| instance.instance.get_progress());
253
254    if let Some(progress) = progress {
255        unsafe {
256            *progress_pct = (progress.value * 100.0) as u32;
257        }
258
259        match progress.detail {
260            Some(s) => s.as_ptr(),
261            None => std::ptr::null(),
262        }
263    } else {
264        unsafe {
265            *progress_pct = 0;
266        }
267
268        std::ptr::null()
269    }
270}
271
272/// # Safety
273///
274/// All pointers must be valid
275pub unsafe extern "C-unwind" fn plugin_event_to_string<T: SourcePlugin>(
276    plugin: *mut ss_plugin_t,
277    event: *const ss_plugin_event_input,
278) -> *const c_char {
279    let plugin = plugin as *mut PluginWrapper<T>;
280    unsafe {
281        let Some(plugin) = plugin.as_mut() else {
282            return std::ptr::null();
283        };
284        let Some(actual_plugin) = &mut plugin.plugin else {
285            return std::ptr::null();
286        };
287
288        let Some(event) = event.as_ref() else {
289            return std::ptr::null();
290        };
291        let event = EventInput(*event, PhantomData);
292
293        match actual_plugin.plugin.event_to_string(&event) {
294            Ok(s) => {
295                plugin.string_storage = s;
296                plugin.string_storage.as_ptr()
297            }
298            Err(_) => std::ptr::null(),
299        }
300    }
301}
302
303/// # Register a source plugin
304///
305/// This macro must be called at most once in a crate (it generates public functions with fixed
306/// `#[unsafe(no_mangle)]` names) with a type implementing [`SourcePlugin`] as the sole parameter.
307#[macro_export]
308macro_rules! source_plugin {
309    ($ty:ty) => {
310        unsafe impl $crate::internals::source::wrappers::SourcePluginExported for $ty {}
311
312        $crate::wrap_ffi! {
313            #[unsafe(no_mangle)]
314            use $crate::internals::source::wrappers: <$ty>;
315            unsafe fn plugin_next_batch(
316                plugin: *mut falco_plugin::api::ss_plugin_t,
317                instance: *mut falco_plugin::api::ss_instance_t,
318                nevts: *mut u32,
319                evts: *mut *mut *mut falco_plugin::api::ss_plugin_event,
320            ) -> i32;
321            unsafe fn plugin_get_progress(
322                plugin: *mut falco_plugin::api::ss_plugin_t,
323                instance: *mut falco_plugin::api::ss_instance_t,
324                progress_pct: *mut u32,
325            ) -> *const ::std::ffi::c_char;
326            unsafe fn plugin_get_id() -> u32;
327            unsafe fn plugin_get_event_source() -> *const ::std::ffi::c_char;
328            unsafe fn plugin_list_open_params(
329                plugin: *mut falco_plugin::api::ss_plugin_t,
330                rc: *mut i32,
331            ) -> *const ::std::ffi::c_char;
332            unsafe fn plugin_open(
333                plugin: *mut falco_plugin::api::ss_plugin_t,
334                params: *const ::std::ffi::c_char,
335                rc: *mut i32,
336            ) -> *mut falco_plugin::api::ss_instance_t;
337            unsafe fn plugin_close(
338                plugin: *mut falco_plugin::api::ss_plugin_t,
339                instance: *mut falco_plugin::api::ss_instance_t,
340            ) -> ();
341            unsafe fn plugin_event_to_string(
342                plugin: *mut falco_plugin::api::ss_plugin_t,
343                event_input: *const falco_plugin::api::ss_plugin_event_input,
344            ) -> *const std::ffi::c_char;
345        }
346
347        #[allow(dead_code)]
348        fn __typecheck_plugin_source_api() -> falco_plugin::api::plugin_api__bindgen_ty_1 {
349            falco_plugin::api::plugin_api__bindgen_ty_1 {
350                next_batch: Some(plugin_next_batch),
351                get_progress: Some(plugin_get_progress),
352                get_id: Some(plugin_get_id),
353                get_event_source: Some(plugin_get_event_source),
354                list_open_params: Some(plugin_list_open_params),
355                open: Some(plugin_open),
356                close: Some(plugin_close),
357                event_to_string: Some(plugin_event_to_string),
358            }
359        }
360    };
361}