scanner: make plugin interface ffi-safe with handle-based registry
Signed-off-by: NotAShelf <raf@notashelf.dev> Change-Id: I8e4790db3cc29f84f4e0d7d8eff36c2c6a6a6964
This commit is contained in:
parent
4f9c7057ff
commit
f4961c7f95
9 changed files with 93 additions and 44 deletions
|
|
@ -5,5 +5,7 @@ pub mod scanner;
|
|||
|
||||
pub use config::Config;
|
||||
pub use logging::{DaemonLogEntry, LogLevel, RingBufferLogger};
|
||||
pub use scanner::{MetricValue, Scanner, ScannerError};
|
||||
pub use scanner::{
|
||||
get_scanner, register_scanner, MetricValue, Scanner, ScannerCreatorFfi, ScannerError,
|
||||
};
|
||||
pub type Result<T> = std::result::Result<T, ScannerError>;
|
||||
|
|
|
|||
|
|
@ -1,10 +1,24 @@
|
|||
use std::collections::HashMap;
|
||||
use std::os::raw::c_void;
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
use std::sync::LazyLock;
|
||||
use std::sync::Mutex;
|
||||
use std::time::Duration;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use thiserror::Error;
|
||||
|
||||
pub type ScannerBox = Box<dyn Scanner>;
|
||||
pub type ScannerResult = Result<ScannerBox>;
|
||||
pub type ScannerMetrics = HashMap<String, MetricValue>;
|
||||
pub type ScannerCollectionResult = Result<ScannerMetrics>;
|
||||
pub type ScannerCollectFn = Box<dyn Fn() -> ScannerCollectionResult + Send + Sync>;
|
||||
pub type ScannerInitFnMut = Mutex<Box<dyn FnMut(&toml::Value) -> Result<()> + Send>>;
|
||||
pub type ScannerCleanupFnMut = Mutex<Box<dyn FnMut() -> Result<()> + Send>>;
|
||||
pub type ScannerCreatorFfi = unsafe extern "C" fn() -> *mut c_void;
|
||||
pub type ScannerInitFn = unsafe extern "C" fn() -> *mut c_void;
|
||||
pub type ScannerDropFn = unsafe extern "C" fn(*mut c_void);
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum ScannerError {
|
||||
#[error("IO error: {0}")]
|
||||
|
|
@ -43,7 +57,7 @@ impl MetricValue {
|
|||
MetricValue::Boolean(v)
|
||||
}
|
||||
|
||||
pub fn from_str(v: impl Into<String>) -> Self {
|
||||
pub fn from_string(v: impl Into<String>) -> Self {
|
||||
MetricValue::String(v.into())
|
||||
}
|
||||
}
|
||||
|
|
@ -52,28 +66,56 @@ pub trait Scanner: Send + Sync {
|
|||
fn name(&self) -> &'static str;
|
||||
fn interval(&self) -> Duration;
|
||||
fn init(&mut self, config: &toml::Value) -> Result<()>;
|
||||
fn collect(&self) -> Result<HashMap<String, MetricValue>>;
|
||||
fn collect(&self) -> ScannerCollectionResult;
|
||||
fn cleanup(&mut self) -> Result<()>;
|
||||
}
|
||||
|
||||
static SCANNER_HANDLES: LazyLock<Mutex<HashMap<usize, ScannerBox>>> =
|
||||
LazyLock::new(|| Mutex::new(HashMap::new()));
|
||||
static NEXT_HANDLE: AtomicUsize = AtomicUsize::new(1);
|
||||
|
||||
#[inline]
|
||||
/// Register a scanner and return a handle.
|
||||
/// # Safety
|
||||
/// The handle must only be used with `get_scanner` from the same process.
|
||||
pub unsafe fn register_scanner(scanner: ScannerBox) -> usize {
|
||||
let handle = NEXT_HANDLE.fetch_add(1, Ordering::SeqCst);
|
||||
SCANNER_HANDLES.lock().unwrap().insert(handle, scanner);
|
||||
handle
|
||||
}
|
||||
|
||||
#[inline]
|
||||
/// Retrieve a scanner by handle, consuming the registration.
|
||||
/// # Safety
|
||||
/// The handle must have been obtained from `register_scanner` in this process,
|
||||
/// and the returned Box must be properly dropped.
|
||||
pub unsafe fn get_scanner(handle: *mut c_void) -> Option<ScannerBox> {
|
||||
let handle = handle as usize;
|
||||
if handle == 0 {
|
||||
None
|
||||
} else {
|
||||
SCANNER_HANDLES.lock().unwrap().remove(&handle)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ScannerRegistry: Send + Sync {
|
||||
fn list_scanners(&self) -> Vec<&'static str>;
|
||||
fn get_scanner(&self, name: &str) -> Option<Box<dyn Scanner>>;
|
||||
fn get_scanner(&self, name: &str) -> Option<ScannerBox>;
|
||||
}
|
||||
|
||||
pub struct DynamicScanner {
|
||||
name: &'static str,
|
||||
interval: Duration,
|
||||
collect_fn: Box<dyn Fn() -> Result<HashMap<String, MetricValue>> + Send + Sync>,
|
||||
init_fn: Mutex<Box<dyn FnMut(&toml::Value) -> Result<()> + Send>>,
|
||||
cleanup_fn: Mutex<Box<dyn FnMut() -> Result<()> + Send>>,
|
||||
collect_fn: ScannerCollectFn,
|
||||
init_fn: ScannerInitFnMut,
|
||||
cleanup_fn: ScannerCleanupFnMut,
|
||||
}
|
||||
|
||||
impl DynamicScanner {
|
||||
pub fn new(
|
||||
name: &'static str,
|
||||
interval: Duration,
|
||||
collect_fn: impl Fn() -> Result<HashMap<String, MetricValue>> + Send + Sync + 'static,
|
||||
collect_fn: impl Fn() -> ScannerCollectionResult + Send + Sync + 'static,
|
||||
init_fn: impl FnMut(&toml::Value) -> Result<()> + Send + 'static,
|
||||
cleanup_fn: impl FnMut() -> Result<()> + Send + 'static,
|
||||
) -> Self {
|
||||
|
|
@ -101,7 +143,7 @@ impl Scanner for DynamicScanner {
|
|||
(init_fn)(config)
|
||||
}
|
||||
|
||||
fn collect(&self) -> Result<HashMap<String, MetricValue>> {
|
||||
fn collect(&self) -> ScannerCollectionResult {
|
||||
(self.collect_fn)()
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue