falco_plugin/plugin/base/
wrappers.rs1use crate::base::Plugin;
2use crate::plugin::base::logger::{FalcoPluginLoggerImpl, FALCO_LOGGER};
3use crate::plugin::base::PluginWrapper;
4use crate::plugin::error::ffi_result::FfiResult;
5use crate::plugin::error::last_error::LastError;
6use crate::plugin::schema::{ConfigSchema, ConfigSchemaType};
7use crate::plugin::tables::vtable::TablesInput;
8use crate::strings::from_ptr::try_str_from_ptr;
9use anyhow::Context;
10use falco_plugin_api::{
11 ss_plugin_init_input, ss_plugin_metric, ss_plugin_rc, ss_plugin_rc_SS_PLUGIN_FAILURE,
12 ss_plugin_rc_SS_PLUGIN_SUCCESS, ss_plugin_t,
13};
14use std::collections::BTreeMap;
15use std::ffi::{c_char, CString};
16use std::sync::Mutex;
17
18#[diagnostic::on_unimplemented(
26 message = "Plugin is not exported",
27 note = "use either `plugin!` or `static_plugin!`"
28)]
29pub unsafe trait BasePluginExported {}
30
31pub extern "C-unwind" fn plugin_get_required_api_version<
32 const MAJOR: usize,
33 const MINOR: usize,
34 const PATCH: usize,
35>() -> *const c_char {
36 static VERSIONS: Mutex<BTreeMap<(usize, usize, usize), CString>> = Mutex::new(BTreeMap::new());
37
38 let mut version = VERSIONS.lock().unwrap();
39 version
42 .entry((MAJOR, MINOR, PATCH))
43 .or_insert_with(|| {
44 let version = format!("{}.{}.{}", MAJOR, MINOR, PATCH);
45 CString::new(version).unwrap()
46 })
47 .as_ptr()
48}
49
50pub extern "C-unwind" fn plugin_get_version<T: Plugin>() -> *const c_char {
51 T::PLUGIN_VERSION.as_ptr()
52}
53
54pub extern "C-unwind" fn plugin_get_name<T: Plugin>() -> *const c_char {
55 T::NAME.as_ptr()
56}
57
58pub extern "C-unwind" fn plugin_get_description<T: Plugin>() -> *const c_char {
59 T::DESCRIPTION.as_ptr()
60}
61
62pub extern "C-unwind" fn plugin_get_contact<T: Plugin>() -> *const c_char {
63 T::CONTACT.as_ptr()
64}
65
66pub unsafe extern "C-unwind" fn plugin_init<P: Plugin>(
70 init_input: *const ss_plugin_init_input,
71 rc: *mut ss_plugin_rc,
72) -> *mut falco_plugin_api::ss_plugin_t {
73 let res = (|| -> Result<*mut PluginWrapper<P>, anyhow::Error> {
74 let init_input = unsafe { init_input.as_ref() }
75 .ok_or_else(|| anyhow::anyhow!("Got empty init_input"))?;
76
77 let init_config =
78 try_str_from_ptr(&init_input.config).context("Failed to get config string")?;
79
80 let config = P::ConfigType::from_str(init_config).context("Failed to parse config")?;
81 if let Some(log_fn) = init_input.log_fn {
82 let logger_impl = FalcoPluginLoggerImpl {
83 owner: init_input.owner,
84 logger_fn: log_fn,
85 };
86
87 *FALCO_LOGGER.inner.write().unwrap() = Some(logger_impl);
88 log::set_logger(&FALCO_LOGGER).ok();
89
90 #[cfg(debug_assertions)]
91 log::set_max_level(log::LevelFilter::Trace);
92
93 #[cfg(not(debug_assertions))]
94 log::set_max_level(log::LevelFilter::Info);
95 }
96
97 let tables_input =
98 TablesInput::try_from(init_input).context("Failed to build tables input")?;
99
100 let last_error = LastError::from(init_input)?;
101
102 P::new(tables_input.as_ref(), config)
103 .map(|plugin| Box::into_raw(Box::new(PluginWrapper::new(plugin, last_error))))
104 })();
105
106 match res {
107 Ok(plugin) => {
108 *rc = ss_plugin_rc_SS_PLUGIN_SUCCESS;
109 plugin.cast()
110 }
111 Err(e) => {
112 let error_str = format!("{:#}", &e);
113 log::error!("Failed to initialize plugin: {}", error_str);
114 let plugin = Box::new(PluginWrapper::<P>::new_error(error_str));
115 *rc = e.status_code();
116 Box::into_raw(plugin).cast()
117 }
118 }
119}
120
121pub unsafe extern "C-unwind" fn plugin_get_init_schema<P: Plugin>(
125 schema_type: *mut falco_plugin_api::ss_plugin_schema_type,
126) -> *const c_char {
127 let Some(schema_type) = schema_type.as_mut() else {
128 return std::ptr::null();
129 };
130 match P::ConfigType::get_schema() {
131 ConfigSchemaType::None => {
132 *schema_type = falco_plugin_api::ss_plugin_schema_type_SS_PLUGIN_SCHEMA_NONE;
133 std::ptr::null()
134 }
135 ConfigSchemaType::Json(s) => {
136 *schema_type = falco_plugin_api::ss_plugin_schema_type_SS_PLUGIN_SCHEMA_JSON;
137 s.as_ptr()
138 }
139 }
140}
141
142pub unsafe extern "C-unwind" fn plugin_destroy<P: Plugin>(
146 plugin: *mut falco_plugin_api::ss_plugin_t,
147) {
148 unsafe {
149 let plugin = plugin as *mut PluginWrapper<P>;
150 let _ = Box::from_raw(plugin);
151 }
152}
153
154pub unsafe extern "C-unwind" fn plugin_get_last_error<P: Plugin>(
158 plugin: *mut falco_plugin_api::ss_plugin_t,
159) -> *const c_char {
160 let plugin = plugin as *mut PluginWrapper<P>;
161 match unsafe { plugin.as_mut() } {
162 Some(plugin) => plugin.error_buf.as_ptr(),
163 None => c"no instance".as_ptr(),
164 }
165}
166
167pub unsafe extern "C-unwind" fn plugin_set_config<P: Plugin>(
168 plugin: *mut falco_plugin_api::ss_plugin_t,
169 config_input: *const falco_plugin_api::ss_plugin_set_config_input,
170) -> falco_plugin_api::ss_plugin_rc {
171 let plugin = plugin as *mut PluginWrapper<P>;
172 let Some(plugin) = plugin.as_mut() else {
173 return ss_plugin_rc_SS_PLUGIN_FAILURE;
174 };
175
176 let Some(ref mut actual_plugin) = &mut plugin.plugin else {
177 return ss_plugin_rc_SS_PLUGIN_FAILURE;
178 };
179
180 let res = (|| -> Result<(), anyhow::Error> {
181 let config_input = unsafe { config_input.as_ref() }.context("Got NULL config")?;
182
183 let updated_config =
184 try_str_from_ptr(&config_input.config).context("Failed to get config string")?;
185 let config = P::ConfigType::from_str(updated_config).context("Failed to parse config")?;
186
187 actual_plugin.plugin.set_config(config)
188 })();
189
190 res.rc(&mut plugin.error_buf)
191}
192
193pub unsafe extern "C-unwind" fn plugin_get_metrics<P: Plugin>(
194 plugin: *mut ss_plugin_t,
195 num_metrics: *mut u32,
196) -> *mut ss_plugin_metric {
197 let plugin = plugin as *mut PluginWrapper<P>;
198 let Some(plugin) = plugin.as_mut() else {
199 *num_metrics = 0;
200 return std::ptr::null_mut();
201 };
202 let Some(ref mut actual_plugin) = &mut plugin.plugin else {
203 *num_metrics = 0;
204 return std::ptr::null_mut();
205 };
206 let Some(num_metrics) = num_metrics.as_mut() else {
207 *num_metrics = 0;
208 return std::ptr::null_mut();
209 };
210
211 plugin.metric_storage.clear();
212 for metric in actual_plugin.plugin.get_metrics() {
213 plugin.metric_storage.push(metric.as_raw());
214 }
215
216 *num_metrics = plugin.metric_storage.len() as u32;
217 plugin.metric_storage.as_ptr().cast_mut()
218}
219
220#[doc(hidden)]
221#[macro_export]
222macro_rules! wrap_ffi {
223 (
224 #[$attr:meta]
225 use $mod:path: <$ty:ty>;
226
227 $(unsafe fn $name:ident( $($param:ident: $param_ty:ty),* $(,)*) -> $ret:ty;)*
228 ) => {
229 $(
230 #[$attr]
231 pub unsafe extern "C-unwind" fn $name ( $($param: $param_ty),*) -> $ret {
232 use $mod as wrappers;
233
234 wrappers::$name::<$ty>($($param),*)
235 }
236 )*
237 }
238}
239
240#[macro_export]
319macro_rules! plugin {
320 (unsafe { $maj:expr; $min:expr; $patch:expr } => #[no_capabilities] $ty:ty) => {
321 unsafe impl $crate::internals::base::wrappers::BasePluginExported for $ty {}
322
323 $crate::base_plugin_ffi_wrappers!($maj; $min; $patch => #[no_mangle] $ty);
324 };
325 (unsafe { $maj:expr; $min:expr; $patch:expr } => $ty:ty) => {
326 plugin!(unsafe {$maj; $min; $patch} => #[no_capabilities] $ty);
327
328 $crate::ensure_plugin_capabilities!($ty);
329 };
330 ($(#[$attr:tt])? $ty:ty) => {
331 plugin!(
332 unsafe {
333 falco_plugin::api::PLUGIN_API_VERSION_MAJOR as usize;
334 falco_plugin::api::PLUGIN_API_VERSION_MINOR as usize;
335 0
336 } => $(#[$attr])? $ty
337 );
338 };
339}
340
341#[macro_export]
435macro_rules! static_plugin {
436 ($(#[$attr:tt])? $name:ident = $ty:ty) => {
437 static_plugin!(
438 $name @ unsafe {
439 falco_plugin::api::PLUGIN_API_VERSION_MAJOR as usize;
440 falco_plugin::api::PLUGIN_API_VERSION_MINOR as usize;
441 0
442 }
443 = $(#[$attr])? $ty
444 );
445 };
446 ($name:ident @ unsafe { $maj:expr; $min:expr; $patch:expr } = #[no_capabilities] $ty:ty) => {
447 #[no_mangle]
448 static $name: falco_plugin::api::plugin_api = const {
449 $crate::base_plugin_ffi_wrappers!($maj; $min; $patch => #[deny(dead_code)] $ty);
450 __plugin_base_api()
451 };
452
453 unsafe impl $crate::internals::base::wrappers::BasePluginExported for $ty {}
455 unsafe impl $crate::internals::async_event::wrappers::AsyncPluginExported for $ty {}
456 unsafe impl $crate::internals::extract::wrappers::ExtractPluginExported for $ty {}
457 unsafe impl $crate::internals::listen::wrappers::CaptureListenPluginExported for $ty {}
458 unsafe impl $crate::internals::parse::wrappers::ParsePluginExported for $ty {}
459 unsafe impl $crate::internals::source::wrappers::SourcePluginExported for $ty {}
460 };
461 ($name:ident @ unsafe { $maj:expr; $min:expr; $patch:expr } = $ty:ty) => {
462 static_plugin!($name @ unsafe { $maj; $min; $patch } = #[no_capabilities] $ty);
463
464 $crate::ensure_plugin_capabilities!($ty);
465 }
466}
467
468#[doc(hidden)]
469#[macro_export]
470macro_rules! ensure_plugin_capabilities {
471 ($ty:ty) => {
472 const _: () = {
473 use $crate::internals::async_event::wrappers::AsyncPluginFallbackApi;
474 use $crate::internals::extract::wrappers::ExtractPluginFallbackApi;
475 use $crate::internals::listen::wrappers::CaptureListenFallbackApi;
476 use $crate::internals::parse::wrappers::ParsePluginFallbackApi;
477 use $crate::internals::source::wrappers::SourcePluginFallbackApi;
478
479 let impls_async =
480 $crate::internals::async_event::wrappers::AsyncPluginApi::<$ty>::IMPLEMENTS_ASYNC;
481 let impls_extract =
482 $crate::internals::extract::wrappers::ExtractPluginApi::<$ty>::IMPLEMENTS_EXTRACT;
483 let impls_listen =
484 $crate::internals::listen::wrappers::CaptureListenApi::<$ty>::IMPLEMENTS_LISTEN;
485 let impls_parse =
486 $crate::internals::parse::wrappers::ParsePluginApi::<$ty>::IMPLEMENTS_PARSE;
487 let impls_source =
488 $crate::internals::source::wrappers::SourcePluginApi::<$ty>::IMPLEMENTS_SOURCE;
489
490 assert!(
491 impls_async || impls_extract || impls_listen || impls_parse || impls_source,
492 "Plugin must implement at least one capability. If you really want a plugin without capabilities, use the #[no_capabilities] attribute"
493 );
494 };
495 };
496}
497
498#[doc(hidden)]
499#[macro_export]
500macro_rules! base_plugin_ffi_wrappers {
501 ($maj:expr; $min:expr; $patch:expr => #[$attr:meta] $ty:ty) => {
502 #[$attr]
503 pub extern "C-unwind" fn plugin_get_required_api_version() -> *const std::ffi::c_char {
504 $crate::internals::base::wrappers::plugin_get_required_api_version::<
505 { $maj },
506 { $min },
507 { $patch },
508 >()
509 }
510
511 $crate::wrap_ffi! {
512 #[$attr]
513 use $crate::internals::base::wrappers: <$ty>;
514
515 unsafe fn plugin_get_version() -> *const std::ffi::c_char;
516 unsafe fn plugin_get_name() -> *const std::ffi::c_char;
517 unsafe fn plugin_get_description() -> *const std::ffi::c_char;
518 unsafe fn plugin_get_contact() -> *const std::ffi::c_char;
519 unsafe fn plugin_get_init_schema(schema_type: *mut u32) -> *const std::ffi::c_char;
520 unsafe fn plugin_init(
521 args: *const falco_plugin::api::ss_plugin_init_input,
522 rc: *mut i32,
523 ) -> *mut falco_plugin::api::ss_plugin_t;
524 unsafe fn plugin_destroy(plugin: *mut falco_plugin::api::ss_plugin_t) -> ();
525 unsafe fn plugin_get_last_error(
526 plugin: *mut falco_plugin::api::ss_plugin_t,
527 ) -> *const std::ffi::c_char;
528 unsafe fn plugin_set_config(
529 plugin: *mut falco_plugin::api::ss_plugin_t,
530 config_input: *const falco_plugin::api::ss_plugin_set_config_input,
531 ) -> falco_plugin::api::ss_plugin_rc;
532 unsafe fn plugin_get_metrics(
533 plugin: *mut falco_plugin::api::ss_plugin_t,
534 num_metrics: *mut u32,
535 ) -> *mut falco_plugin::api::ss_plugin_metric;
536 }
537
538 #[allow(dead_code)]
539 pub const fn __plugin_base_api() -> falco_plugin::api::plugin_api {
540 use $crate::internals::async_event::wrappers::AsyncPluginFallbackApi;
541 use $crate::internals::extract::wrappers::ExtractPluginFallbackApi;
542 use $crate::internals::listen::wrappers::CaptureListenFallbackApi;
543 use $crate::internals::parse::wrappers::ParsePluginFallbackApi;
544 use $crate::internals::source::wrappers::SourcePluginFallbackApi;
545 falco_plugin::api::plugin_api {
546 get_required_api_version: Some(plugin_get_required_api_version),
547 get_version: Some(plugin_get_version),
548 get_name: Some(plugin_get_name),
549 get_description: Some(plugin_get_description),
550 get_contact: Some(plugin_get_contact),
551 get_init_schema: Some(plugin_get_init_schema),
552 init: Some(plugin_init),
553 destroy: Some(plugin_destroy),
554 get_last_error: Some(plugin_get_last_error),
555 __bindgen_anon_1:
556 $crate::internals::source::wrappers::SourcePluginApi::<$ty>::SOURCE_API,
557 __bindgen_anon_2:
558 $crate::internals::extract::wrappers::ExtractPluginApi::<$ty>::EXTRACT_API,
559 __bindgen_anon_3:
560 $crate::internals::parse::wrappers::ParsePluginApi::<$ty>::PARSE_API,
561 __bindgen_anon_4:
562 $crate::internals::async_event::wrappers::AsyncPluginApi::<$ty>::ASYNC_API,
563 __bindgen_anon_5:
564 $crate::internals::listen::wrappers::CaptureListenApi::<$ty>::LISTEN_API,
565 set_config: Some(plugin_set_config),
566 get_metrics: Some(plugin_get_metrics),
567 }
568 }
569 };
570}