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 = unsafe { 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 unsafe {
109 *rc = ss_plugin_rc_SS_PLUGIN_SUCCESS;
110 }
111 plugin.cast()
112 }
113 Err(e) => {
114 let error_str = format!("{:#}", &e);
115 log::error!("Failed to initialize plugin: {error_str}");
116 let plugin = Box::new(PluginWrapper::<P>::new_error(error_str));
117 unsafe {
118 *rc = e.status_code();
119 }
120 Box::into_raw(plugin).cast()
121 }
122 }
123}
124
125pub unsafe extern "C-unwind" fn plugin_get_init_schema<P: Plugin>(
129 schema_type: *mut falco_plugin_api::ss_plugin_schema_type,
130) -> *const c_char {
131 let schema_type = unsafe {
132 let Some(schema_type) = schema_type.as_mut() else {
133 return std::ptr::null();
134 };
135 schema_type
136 };
137 match P::ConfigType::get_schema() {
138 ConfigSchemaType::None => {
139 *schema_type = falco_plugin_api::ss_plugin_schema_type_SS_PLUGIN_SCHEMA_NONE;
140 std::ptr::null()
141 }
142 ConfigSchemaType::Json(s) => {
143 *schema_type = falco_plugin_api::ss_plugin_schema_type_SS_PLUGIN_SCHEMA_JSON;
144 s.as_ptr()
145 }
146 }
147}
148
149pub unsafe extern "C-unwind" fn plugin_destroy<P: Plugin>(
153 plugin: *mut falco_plugin_api::ss_plugin_t,
154) {
155 unsafe {
156 let plugin = plugin as *mut PluginWrapper<P>;
157 let _ = Box::from_raw(plugin);
158 }
159}
160
161pub unsafe extern "C-unwind" fn plugin_get_last_error<P: Plugin>(
165 plugin: *mut falco_plugin_api::ss_plugin_t,
166) -> *const c_char {
167 let plugin = plugin as *mut PluginWrapper<P>;
168 match unsafe { plugin.as_mut() } {
169 Some(plugin) => plugin.error_buf.as_ptr(),
170 None => c"no instance".as_ptr(),
171 }
172}
173
174pub unsafe extern "C-unwind" fn plugin_set_config<P: Plugin>(
175 plugin: *mut falco_plugin_api::ss_plugin_t,
176 config_input: *const falco_plugin_api::ss_plugin_set_config_input,
177) -> falco_plugin_api::ss_plugin_rc {
178 let plugin = plugin as *mut PluginWrapper<P>;
179 let plugin = unsafe {
180 let Some(plugin) = plugin.as_mut() else {
181 return ss_plugin_rc_SS_PLUGIN_FAILURE;
182 };
183 plugin
184 };
185
186 let Some(actual_plugin) = &mut plugin.plugin else {
187 return ss_plugin_rc_SS_PLUGIN_FAILURE;
188 };
189
190 let res = (|| -> Result<(), anyhow::Error> {
191 let config_input = unsafe { config_input.as_ref() }.context("Got NULL config")?;
192
193 let updated_config =
194 try_str_from_ptr(&config_input.config).context("Failed to get config string")?;
195 let config = P::ConfigType::from_str(updated_config).context("Failed to parse config")?;
196
197 actual_plugin.plugin.set_config(config)
198 })();
199
200 res.rc(&mut plugin.error_buf)
201}
202
203pub unsafe extern "C-unwind" fn plugin_get_metrics<P: Plugin>(
204 plugin: *mut ss_plugin_t,
205 num_metrics: *mut u32,
206) -> *mut ss_plugin_metric {
207 let plugin = plugin as *mut PluginWrapper<P>;
208 let num_metrics = unsafe {
209 let Some(num_metrics) = num_metrics.as_mut() else {
210 return std::ptr::null_mut();
211 };
212 num_metrics
213 };
214
215 let plugin = unsafe {
216 let Some(plugin) = plugin.as_mut() else {
217 *num_metrics = 0;
218 return std::ptr::null_mut();
219 };
220 plugin
221 };
222
223 let Some(actual_plugin) = &mut plugin.plugin else {
224 *num_metrics = 0;
225 return std::ptr::null_mut();
226 };
227
228 plugin.metric_storage.clear();
229 for metric in actual_plugin.plugin.get_metrics() {
230 plugin.metric_storage.push(metric.as_raw());
231 }
232
233 *num_metrics = plugin.metric_storage.len() as u32;
234 plugin.metric_storage.as_ptr().cast_mut()
235}
236
237#[doc(hidden)]
238#[macro_export]
239macro_rules! wrap_ffi {
240 (
241 #[$attr:meta]
242 use $mod:path: <$ty:ty>;
243
244 $(unsafe fn $name:ident( $($param:ident: $param_ty:ty),* $(,)*) -> $ret:ty;)*
245 ) => {
246 $(
247 #[$attr]
248 pub unsafe extern "C-unwind" fn $name ( $($param: $param_ty),*) -> $ret {
249 use $mod as wrappers;
250
251 wrappers::$name::<$ty>($($param),*)
252 }
253 )*
254 }
255}
256
257#[macro_export]
336macro_rules! plugin {
337 (unsafe { $maj:expr; $min:expr; $patch:expr } => #[no_capabilities] $ty:ty) => {
338 unsafe impl $crate::internals::base::wrappers::BasePluginExported for $ty {}
339
340 $crate::base_plugin_ffi_wrappers!($maj; $min; $patch => #[unsafe(no_mangle)] $ty);
341 };
342 (unsafe { $maj:expr; $min:expr; $patch:expr } => $ty:ty) => {
343 plugin!(unsafe {$maj; $min; $patch} => #[no_capabilities] $ty);
344
345 $crate::ensure_plugin_capabilities!($ty);
346 };
347 ($(#[$attr:tt])? $ty:ty) => {
348 plugin!(
349 unsafe {
350 falco_plugin::api::PLUGIN_API_VERSION_MAJOR as usize;
351 falco_plugin::api::PLUGIN_API_VERSION_MINOR as usize;
352 0
353 } => $(#[$attr])? $ty
354 );
355 };
356}
357
358#[macro_export]
452macro_rules! static_plugin {
453 ($(#[$attr:tt])? $vis:vis $name:ident = $ty:ty) => {
454 static_plugin!(
455 $vis $name @ unsafe {
456 falco_plugin::api::PLUGIN_API_VERSION_MAJOR as usize;
457 falco_plugin::api::PLUGIN_API_VERSION_MINOR as usize;
458 0
459 }
460 = $(#[$attr])? $ty
461 );
462 };
463 ($vis:vis $name:ident @ unsafe { $maj:expr; $min:expr; $patch:expr } = #[no_capabilities] $ty:ty) => {
464 #[unsafe(no_mangle)]
465 $vis static $name: falco_plugin::api::plugin_api = const {
466 $crate::base_plugin_ffi_wrappers!($maj; $min; $patch => #[deny(dead_code)] $ty);
467 __plugin_base_api()
468 };
469
470 unsafe impl $crate::internals::base::wrappers::BasePluginExported for $ty {}
472 unsafe impl $crate::internals::async_event::wrappers::AsyncPluginExported for $ty {}
473 unsafe impl $crate::internals::extract::wrappers::ExtractPluginExported for $ty {}
474 unsafe impl $crate::internals::listen::wrappers::CaptureListenPluginExported for $ty {}
475 unsafe impl $crate::internals::parse::wrappers::ParsePluginExported for $ty {}
476 unsafe impl $crate::internals::source::wrappers::SourcePluginExported for $ty {}
477 };
478 ($vis:vis $name:ident @ unsafe { $maj:expr; $min:expr; $patch:expr } = $ty:ty) => {
479 static_plugin!($vis $name @ unsafe { $maj; $min; $patch } = #[no_capabilities] $ty);
480
481 $crate::ensure_plugin_capabilities!($ty);
482 }
483}
484
485#[doc(hidden)]
486#[macro_export]
487macro_rules! ensure_plugin_capabilities {
488 ($ty:ty) => {
489 const _: () = {
490 use $crate::internals::async_event::wrappers::AsyncPluginFallbackApi;
491 use $crate::internals::extract::wrappers::ExtractPluginFallbackApi;
492 use $crate::internals::listen::wrappers::CaptureListenFallbackApi;
493 use $crate::internals::parse::wrappers::ParsePluginFallbackApi;
494 use $crate::internals::source::wrappers::SourcePluginFallbackApi;
495
496 let impls_async =
497 $crate::internals::async_event::wrappers::AsyncPluginApi::<$ty>::IMPLEMENTS_ASYNC;
498 let impls_extract =
499 $crate::internals::extract::wrappers::ExtractPluginApi::<$ty>::IMPLEMENTS_EXTRACT;
500 let impls_listen =
501 $crate::internals::listen::wrappers::CaptureListenApi::<$ty>::IMPLEMENTS_LISTEN;
502 let impls_parse =
503 $crate::internals::parse::wrappers::ParsePluginApi::<$ty>::IMPLEMENTS_PARSE;
504 let impls_source =
505 $crate::internals::source::wrappers::SourcePluginApi::<$ty>::IMPLEMENTS_SOURCE;
506
507 assert!(
508 impls_async || impls_extract || impls_listen || impls_parse || impls_source,
509 "Plugin must implement at least one capability. If you really want a plugin without capabilities, use the #[no_capabilities] attribute"
510 );
511 };
512 };
513}
514
515#[doc(hidden)]
516#[macro_export]
517macro_rules! base_plugin_ffi_wrappers {
518 ($maj:expr; $min:expr; $patch:expr => #[$attr:meta] $ty:ty) => {
519 #[$attr]
520 pub extern "C-unwind" fn plugin_get_required_api_version() -> *const std::ffi::c_char {
521 $crate::internals::base::wrappers::plugin_get_required_api_version::<
522 { $maj },
523 { $min },
524 { $patch },
525 >()
526 }
527
528 $crate::wrap_ffi! {
529 #[$attr]
530 use $crate::internals::base::wrappers: <$ty>;
531
532 unsafe fn plugin_get_version() -> *const std::ffi::c_char;
533 unsafe fn plugin_get_name() -> *const std::ffi::c_char;
534 unsafe fn plugin_get_description() -> *const std::ffi::c_char;
535 unsafe fn plugin_get_contact() -> *const std::ffi::c_char;
536 unsafe fn plugin_get_init_schema(schema_type: *mut u32) -> *const std::ffi::c_char;
537 unsafe fn plugin_init(
538 args: *const falco_plugin::api::ss_plugin_init_input,
539 rc: *mut i32,
540 ) -> *mut falco_plugin::api::ss_plugin_t;
541 unsafe fn plugin_destroy(plugin: *mut falco_plugin::api::ss_plugin_t) -> ();
542 unsafe fn plugin_get_last_error(
543 plugin: *mut falco_plugin::api::ss_plugin_t,
544 ) -> *const std::ffi::c_char;
545 unsafe fn plugin_set_config(
546 plugin: *mut falco_plugin::api::ss_plugin_t,
547 config_input: *const falco_plugin::api::ss_plugin_set_config_input,
548 ) -> falco_plugin::api::ss_plugin_rc;
549 unsafe fn plugin_get_metrics(
550 plugin: *mut falco_plugin::api::ss_plugin_t,
551 num_metrics: *mut u32,
552 ) -> *mut falco_plugin::api::ss_plugin_metric;
553 }
554
555 #[allow(dead_code)]
556 pub const fn __plugin_base_api() -> falco_plugin::api::plugin_api {
557 use $crate::internals::async_event::wrappers::AsyncPluginFallbackApi;
558 use $crate::internals::extract::wrappers::ExtractPluginFallbackApi;
559 use $crate::internals::listen::wrappers::CaptureListenFallbackApi;
560 use $crate::internals::parse::wrappers::ParsePluginFallbackApi;
561 use $crate::internals::source::wrappers::SourcePluginFallbackApi;
562 falco_plugin::api::plugin_api {
563 get_required_api_version: Some(plugin_get_required_api_version),
564 get_version: Some(plugin_get_version),
565 get_name: Some(plugin_get_name),
566 get_description: Some(plugin_get_description),
567 get_contact: Some(plugin_get_contact),
568 get_init_schema: Some(plugin_get_init_schema),
569 init: Some(plugin_init),
570 destroy: Some(plugin_destroy),
571 get_last_error: Some(plugin_get_last_error),
572 __bindgen_anon_1:
573 $crate::internals::source::wrappers::SourcePluginApi::<$ty>::SOURCE_API,
574 __bindgen_anon_2:
575 $crate::internals::extract::wrappers::ExtractPluginApi::<$ty>::EXTRACT_API,
576 __bindgen_anon_3:
577 $crate::internals::parse::wrappers::ParsePluginApi::<$ty>::PARSE_API,
578 __bindgen_anon_4:
579 $crate::internals::async_event::wrappers::AsyncPluginApi::<$ty>::ASYNC_API,
580 __bindgen_anon_5:
581 $crate::internals::listen::wrappers::CaptureListenApi::<$ty>::LISTEN_API,
582 set_config: Some(plugin_set_config),
583 get_metrics: Some(plugin_get_metrics),
584 }
585 }
586 };
587}