Skip to main content

cradle_plugin_api/
lib.rs

1//! # `cradle-plugin-api`
2//!
3//! This crate acts as the API for developing plugins for cradle
4#![warn(missing_docs)]
5/// Main definitions in use by plugins
6pub mod checks;
7/// FFI <--> Non-FFI conversion functions
8pub mod convert;
9/// FFI-safe type definitions
10pub mod ffi;
11/// Logging functions for plugins
12pub mod log;
13
14pub use cradle_shared::{CheckResult, CradleError, CradleMutex, CradleResult, Severity};
15/// Plugin major API version
16pub const CRADLE_PLUGIN_API_MAJOR: u16 = 1;
17/// Plugin minor API version
18pub const CRADLE_PLUGIN_API_MINOR: u16 = 1;
19
20/// Plugin API version
21pub const CRADLE_PLUGIN_API_VERSION: u32 =
22    ((CRADLE_PLUGIN_API_MAJOR as u32) << 16) | CRADLE_PLUGIN_API_MINOR as u32;
23
24pub use checks::*;
25pub use convert::{
26    check_results_to_ffi, ffi_event_to_agent_event, ffi_string_array_to_vec, free_ffi_results,
27    free_ffi_string, free_ffi_string_array, string_to_ffi_ptr, strings_to_ffi_array,
28};
29pub use cradle_hooks;
30use cradle_hooks::engine::HookEngine;
31pub use ffi::*;
32pub use log::*;
33pub use paste::paste;
34
35/// Helper macro to register a plugin
36/// Builds up the needed exported functions for the plugin to run
37#[macro_export]
38macro_rules! register_plugin {
39    ($plugin_ty:ty) => {
40        static __CRADLE_PLUGIN: std::sync::OnceLock<std::sync::Mutex<$plugin_ty>> =
41            std::sync::OnceLock::new();
42
43        #[unsafe(no_mangle)]
44        pub extern "C" fn cradle_check_api_version() -> u32 {
45            $crate::CRADLE_PLUGIN_API_VERSION
46        }
47
48        #[unsafe(no_mangle)]
49        pub extern "C" fn cradle_plugin_kind() -> u32 {
50            1
51        }
52
53        #[unsafe(no_mangle)]
54        pub extern "C" fn cradle_plugin_name() -> *mut std::os::raw::c_char {
55            let p = __CRADLE_PLUGIN
56                .get_or_init(|| std::sync::Mutex::new(<$plugin_ty as Default>::default()));
57            let name = match p.lock() {
58                Ok(plugin) => plugin.name().to_string(),
59                Err(_) => return std::ptr::null_mut(),
60            };
61            $crate::string_to_ffi_ptr(name)
62        }
63
64        #[unsafe(no_mangle)]
65        pub unsafe extern "C" fn cradle_plugin_free_string(ptr: *mut std::os::raw::c_char) {
66            unsafe {
67                $crate::free_ffi_string(ptr);
68            }
69        }
70
71        #[unsafe(no_mangle)]
72        pub unsafe extern "C" fn cradle_plugin_init(engine_ptr: *mut std::ffi::c_void) -> i32 {
73            if engine_ptr.is_null() {
74                $crate::cradle_log(
75                    "[cradle-plugin-api] plugin init received a null engine pointer",
76                );
77                return -1;
78            }
79
80            let p = __CRADLE_PLUGIN
81                .get_or_init(|| std::sync::Mutex::new(<$plugin_ty as Default>::default()));
82            unsafe {
83                let arc = std::sync::Arc::from_raw(
84                    engine_ptr as *const std::sync::Mutex<$crate::cradle_hooks::engine::HookEngine>,
85                );
86                let arc_clone = std::sync::Arc::clone(&arc);
87                std::mem::forget(arc);
88                let cmutex = $crate::CradleMutex(arc_clone);
89                match p.lock() {
90                    Ok(mut plugin) => match plugin.init(cmutex) {
91                        Ok(()) => 0,
92                        Err(e) => {
93                            $crate::cradle_log(&format!(
94                                "[cradle-plugin-api] failed to load plugin: {e}"
95                            ));
96                            -1
97                        }
98                    },
99                    Err(_) => {
100                        $crate::cradle_log("[cradle-plugin-api] failed to lock plugin during init");
101                        -1
102                    }
103                }
104            }
105        }
106
107        #[unsafe(no_mangle)]
108        pub unsafe extern "C" fn cradle_plugin_on_event(event: *const $crate::FfiEvent) -> i32 {
109            if event.is_null() {
110                $crate::cradle_log("[cradle-plugin-api] plugin received a null event pointer");
111                return -1;
112            }
113
114            let p = __CRADLE_PLUGIN
115                .get_or_init(|| std::sync::Mutex::new(<$plugin_ty as Default>::default()));
116            unsafe {
117                let event = $crate::ffi_event_to_agent_event(&*event);
118                match p.lock() {
119                    Ok(mut plugin) => match plugin.on_event(&event) {
120                        Ok(()) => 0,
121                        Err(e) => {
122                            $crate::cradle_log(&format!(
123                                "[cradle-plugin-api] failed to run plugin: {e}"
124                            ));
125                            -1
126                        }
127                    },
128                    Err(_) => {
129                        $crate::cradle_log(
130                            "[cradle-plugin-api] failed to lock plugin during event handling",
131                        );
132                        -1
133                    }
134                }
135            }
136        }
137
138        #[unsafe(no_mangle)]
139        pub extern "C" fn cradle_plugin_collect() -> $crate::FfiResultArray {
140            let p = __CRADLE_PLUGIN
141                .get_or_init(|| std::sync::Mutex::new(<$plugin_ty as Default>::default()));
142            match p.lock() {
143                Ok(mut plugin) => $crate::check_results_to_ffi(plugin.results()),
144                Err(_) => $crate::FfiResultArray {
145                    ptr: std::ptr::null_mut(),
146                    len: 0,
147                },
148            }
149        }
150
151        #[unsafe(no_mangle)]
152        pub unsafe extern "C" fn cradle_plugin_free_results(arr: $crate::FfiResultArray) {
153            unsafe { $crate::free_ffi_results(arr) }
154        }
155
156        #[unsafe(no_mangle)]
157        pub extern "C" fn cradle_plugin_drain_logs() -> $crate::FfiStringArray {
158            $crate::strings_to_ffi_array($crate::drain_log())
159        }
160
161        #[unsafe(no_mangle)]
162        pub unsafe extern "C" fn cradle_plugin_free_log_array(arr: $crate::FfiStringArray) {
163            unsafe { $crate::free_ffi_string_array(arr) }
164        }
165
166        #[unsafe(no_mangle)]
167        pub extern "C" fn cradle_plugin_cleanup() {
168            let p = __CRADLE_PLUGIN
169                .get_or_init(|| std::sync::Mutex::new(<$plugin_ty as Default>::default()));
170            if let Ok(mut plugin) = p.lock() {
171                plugin.cleanup();
172            }
173        }
174    };
175}
176
177/// Trait definition for a cradle plugin
178pub trait CradlePlugin: Default + Send {
179    /// Name of the plugin
180    fn name(&self) -> &str;
181    /// Optional initialization function
182    /// If your plugin needs to hook onto, or initialize stuff when the agent is being initialized, define this function
183    fn init(&mut self, engine: CradleMutex<HookEngine>) -> CradleResult {
184        let _ = engine;
185        Ok(())
186    }
187    /// Optional event handling function
188    /// If your plugin checks things based on some event (specified by [`AgentEvent`]), then define this function
189    fn on_event(&mut self, event: &AgentEvent) -> CradleResult {
190        let _ = event;
191        Ok(())
192    }
193    /// Returns the array of results ([`CheckResult`])
194    fn results(&mut self) -> Vec<CheckResult>;
195    /// Optional plugin cleanup function
196    fn cleanup(&mut self) {}
197}