1#![warn(missing_docs)]
5pub mod checks;
7pub mod convert;
9pub mod ffi;
11pub mod log;
13
14pub use cradle_shared::{CheckResult, CradleError, CradleMutex, CradleResult, Severity};
15pub const CRADLE_PLUGIN_API_MAJOR: u16 = 1;
17pub const CRADLE_PLUGIN_API_MINOR: u16 = 1;
19
20pub 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#[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
177pub trait CradlePlugin: Default + Send {
179 fn name(&self) -> &str;
181 fn init(&mut self, engine: CradleMutex<HookEngine>) -> CradleResult {
184 let _ = engine;
185 Ok(())
186 }
187 fn on_event(&mut self, event: &AgentEvent) -> CradleResult {
190 let _ = event;
191 Ok(())
192 }
193 fn results(&mut self) -> Vec<CheckResult>;
195 fn cleanup(&mut self) {}
197}