cradle-plugin-api 0.1.3

API for cradle plugins
Documentation
//! # `cradle-plugin-api`
//!
//! This crate acts as the API for developing plugins for cradle
#![warn(missing_docs)]
/// Main definitions in use by plugins
pub mod checks;
/// FFI <--> Non-FFI conversion functions
pub mod convert;
/// FFI-safe type definitions
pub mod ffi;
/// Logging functions for plugins
pub mod log;

pub use cradle_shared::{CheckResult, CradleError, CradleMutex, CradleResult, Severity};
/// Plugin major API version
pub const CRADLE_PLUGIN_API_MAJOR: u16 = 1;
/// Plugin minor API version
pub const CRADLE_PLUGIN_API_MINOR: u16 = 1;

/// Plugin API version
pub const CRADLE_PLUGIN_API_VERSION: u32 =
    ((CRADLE_PLUGIN_API_MAJOR as u32) << 16) | CRADLE_PLUGIN_API_MINOR as u32;

pub use checks::*;
pub use convert::{
    check_results_to_ffi, ffi_event_to_agent_event, ffi_string_array_to_vec, free_ffi_results,
    free_ffi_string, free_ffi_string_array, string_to_ffi_ptr, strings_to_ffi_array,
};
pub use cradle_hooks;
use cradle_hooks::engine::HookEngine;
pub use ffi::*;
pub use log::*;
pub use paste::paste;

/// Helper macro to register a plugin
/// Builds up the needed exported functions for the plugin to run
#[macro_export]
macro_rules! register_plugin {
    ($plugin_ty:ty) => {
        static __CRADLE_PLUGIN: std::sync::OnceLock<std::sync::Mutex<$plugin_ty>> =
            std::sync::OnceLock::new();

        #[unsafe(no_mangle)]
        pub extern "C" fn cradle_check_api_version() -> u32 {
            $crate::CRADLE_PLUGIN_API_VERSION
        }

        #[unsafe(no_mangle)]
        pub extern "C" fn cradle_plugin_kind() -> u32 {
            1
        }

        #[unsafe(no_mangle)]
        pub extern "C" fn cradle_plugin_name() -> *mut std::os::raw::c_char {
            let p = __CRADLE_PLUGIN
                .get_or_init(|| std::sync::Mutex::new(<$plugin_ty as Default>::default()));
            let name = match p.lock() {
                Ok(plugin) => plugin.name().to_string(),
                Err(_) => return std::ptr::null_mut(),
            };
            $crate::string_to_ffi_ptr(name)
        }

        #[unsafe(no_mangle)]
        pub unsafe extern "C" fn cradle_plugin_free_string(ptr: *mut std::os::raw::c_char) {
            unsafe {
                $crate::free_ffi_string(ptr);
            }
        }

        #[unsafe(no_mangle)]
        pub unsafe extern "C" fn cradle_plugin_init(engine_ptr: *mut std::ffi::c_void) -> i32 {
            if engine_ptr.is_null() {
                $crate::cradle_log(
                    "[cradle-plugin-api] plugin init received a null engine pointer",
                );
                return -1;
            }

            let p = __CRADLE_PLUGIN
                .get_or_init(|| std::sync::Mutex::new(<$plugin_ty as Default>::default()));
            unsafe {
                let arc = std::sync::Arc::from_raw(
                    engine_ptr as *const std::sync::Mutex<$crate::cradle_hooks::engine::HookEngine>,
                );
                let arc_clone = std::sync::Arc::clone(&arc);
                std::mem::forget(arc);
                let cmutex = $crate::CradleMutex(arc_clone);
                match p.lock() {
                    Ok(mut plugin) => match plugin.init(cmutex) {
                        Ok(()) => 0,
                        Err(e) => {
                            $crate::cradle_log(&format!(
                                "[cradle-plugin-api] failed to load plugin: {e}"
                            ));
                            -1
                        }
                    },
                    Err(_) => {
                        $crate::cradle_log("[cradle-plugin-api] failed to lock plugin during init");
                        -1
                    }
                }
            }
        }

        #[unsafe(no_mangle)]
        pub unsafe extern "C" fn cradle_plugin_on_event(event: *const $crate::FfiEvent) -> i32 {
            if event.is_null() {
                $crate::cradle_log("[cradle-plugin-api] plugin received a null event pointer");
                return -1;
            }

            let p = __CRADLE_PLUGIN
                .get_or_init(|| std::sync::Mutex::new(<$plugin_ty as Default>::default()));
            unsafe {
                let event = $crate::ffi_event_to_agent_event(&*event);
                match p.lock() {
                    Ok(mut plugin) => match plugin.on_event(&event) {
                        Ok(()) => 0,
                        Err(e) => {
                            $crate::cradle_log(&format!(
                                "[cradle-plugin-api] failed to run plugin: {e}"
                            ));
                            -1
                        }
                    },
                    Err(_) => {
                        $crate::cradle_log(
                            "[cradle-plugin-api] failed to lock plugin during event handling",
                        );
                        -1
                    }
                }
            }
        }

        #[unsafe(no_mangle)]
        pub extern "C" fn cradle_plugin_collect() -> $crate::FfiResultArray {
            let p = __CRADLE_PLUGIN
                .get_or_init(|| std::sync::Mutex::new(<$plugin_ty as Default>::default()));
            match p.lock() {
                Ok(mut plugin) => $crate::check_results_to_ffi(plugin.results()),
                Err(_) => $crate::FfiResultArray {
                    ptr: std::ptr::null_mut(),
                    len: 0,
                },
            }
        }

        #[unsafe(no_mangle)]
        pub unsafe extern "C" fn cradle_plugin_free_results(arr: $crate::FfiResultArray) {
            unsafe { $crate::free_ffi_results(arr) }
        }

        #[unsafe(no_mangle)]
        pub extern "C" fn cradle_plugin_drain_logs() -> $crate::FfiStringArray {
            $crate::strings_to_ffi_array($crate::drain_log())
        }

        #[unsafe(no_mangle)]
        pub unsafe extern "C" fn cradle_plugin_free_log_array(arr: $crate::FfiStringArray) {
            unsafe { $crate::free_ffi_string_array(arr) }
        }

        #[unsafe(no_mangle)]
        pub extern "C" fn cradle_plugin_cleanup() {
            let p = __CRADLE_PLUGIN
                .get_or_init(|| std::sync::Mutex::new(<$plugin_ty as Default>::default()));
            if let Ok(mut plugin) = p.lock() {
                plugin.cleanup();
            }
        }
    };
}

/// Trait definition for a cradle plugin
pub trait CradlePlugin: Default + Send {
    /// Name of the plugin
    fn name(&self) -> &str;
    /// Optional initialization function
    /// If your plugin needs to hook onto, or initialize stuff when the agent is being initialized, define this function
    fn init(&mut self, engine: CradleMutex<HookEngine>) -> CradleResult {
        let _ = engine;
        Ok(())
    }
    /// Optional event handling function
    /// If your plugin checks things based on some event (specified by [`AgentEvent`]), then define this function
    fn on_event(&mut self, event: &AgentEvent) -> CradleResult {
        let _ = event;
        Ok(())
    }
    /// Returns the array of results ([`CheckResult`])
    fn results(&mut self) -> Vec<CheckResult>;
    /// Optional plugin cleanup function
    fn cleanup(&mut self) {}
}