|
@@ -0,0 +1,898 @@
|
|
|
|
|
+// libcmap interface for Rust
|
|
|
|
|
+// Copyright (c) 2021 Red Hat, Inc.
|
|
|
|
|
+//
|
|
|
|
|
+// All rights reserved.
|
|
|
|
|
+//
|
|
|
|
|
+// Author: Christine Caulfield (ccaulfi@redhat.com)
|
|
|
|
|
+//
|
|
|
|
|
+
|
|
|
|
|
+#![allow(clippy::type_complexity)]
|
|
|
|
|
+
|
|
|
|
|
+// For the code generated by bindgen
|
|
|
|
|
+use crate::sys::cmap as ffi;
|
|
|
|
|
+
|
|
|
|
|
+use num_enum::TryFromPrimitive;
|
|
|
|
|
+use std::any::type_name;
|
|
|
|
|
+use std::collections::HashMap;
|
|
|
|
|
+use std::convert::TryFrom;
|
|
|
|
|
+use std::ffi::CString;
|
|
|
|
|
+use std::fmt;
|
|
|
|
|
+use std::os::raw::{c_char, c_int, c_void};
|
|
|
|
|
+use std::ptr::copy_nonoverlapping;
|
|
|
|
|
+use std::sync::Mutex;
|
|
|
|
|
+
|
|
|
|
|
+use crate::string_from_bytes;
|
|
|
|
|
+use crate::{CsError, DispatchFlags, Result};
|
|
|
|
|
+
|
|
|
|
|
+// Maps:
|
|
|
|
|
+/// "Maps" available to [initialize]
|
|
|
|
|
+pub enum Map {
|
|
|
|
|
+ Icmap,
|
|
|
|
|
+ Stats,
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+bitflags! {
|
|
|
|
|
+/// Tracker types for cmap, both passed into [track_add]
|
|
|
|
|
+/// and returned from its callback.
|
|
|
|
|
+ pub struct TrackType: i32
|
|
|
|
|
+ {
|
|
|
|
|
+ const DELETE = 1;
|
|
|
|
|
+ const MODIFY = 2;
|
|
|
|
|
+ const ADD = 4;
|
|
|
|
|
+ const PREFIX = 8;
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+impl fmt::Display for TrackType {
|
|
|
|
|
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
|
+ if self.contains(TrackType::DELETE) {
|
|
|
|
|
+ write!(f, "DELETE ")?
|
|
|
|
|
+ }
|
|
|
|
|
+ if self.contains(TrackType::MODIFY) {
|
|
|
|
|
+ write!(f, "MODIFY ")?
|
|
|
|
|
+ }
|
|
|
|
|
+ if self.contains(TrackType::ADD) {
|
|
|
|
|
+ write!(f, "ADD ")?
|
|
|
|
|
+ }
|
|
|
|
|
+ if self.contains(TrackType::PREFIX) {
|
|
|
|
|
+ write!(f, "PREFIX ")
|
|
|
|
|
+ } else {
|
|
|
|
|
+ Ok(())
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+#[derive(Copy, Clone)]
|
|
|
|
|
+/// A handle returned from [initialize], needs to be passed to all other cmap API calls
|
|
|
|
|
+pub struct Handle {
|
|
|
|
|
+ cmap_handle: u64,
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+#[derive(Copy, Clone)]
|
|
|
|
|
+/// A handle for a specific CMAP tracker. returned from [track_add].
|
|
|
|
|
+/// There may be multiple TrackHandles per [Handle]
|
|
|
|
|
+pub struct TrackHandle {
|
|
|
|
|
+ track_handle: u64,
|
|
|
|
|
+ notify_callback: NotifyCallback,
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// Used to convert CMAP handles into one of ours, for callbacks
|
|
|
|
|
+lazy_static! {
|
|
|
|
|
+ static ref TRACKHANDLE_HASH: Mutex<HashMap<u64, TrackHandle>> = Mutex::new(HashMap::new());
|
|
|
|
|
+ static ref HANDLE_HASH: Mutex<HashMap<u64, Handle>> = Mutex::new(HashMap::new());
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/// Initialize a connection to the cmap subsystem.
|
|
|
|
|
+/// map specifies which cmap "map" to use.
|
|
|
|
|
+/// Returns a [Handle] into the cmap library
|
|
|
|
|
+pub fn initialize(map: Map) -> Result<Handle> {
|
|
|
|
|
+ let mut handle: ffi::cmap_handle_t = 0;
|
|
|
|
|
+ let c_map = match map {
|
|
|
|
|
+ Map::Icmap => ffi::CMAP_MAP_ICMAP,
|
|
|
|
|
+ Map::Stats => ffi::CMAP_MAP_STATS,
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ unsafe {
|
|
|
|
|
+ let res = ffi::cmap_initialize_map(&mut handle, c_map);
|
|
|
|
|
+ if res == ffi::CS_OK {
|
|
|
|
|
+ let rhandle = Handle {
|
|
|
|
|
+ cmap_handle: handle,
|
|
|
|
|
+ };
|
|
|
|
|
+ HANDLE_HASH.lock().unwrap().insert(handle, rhandle);
|
|
|
|
|
+ Ok(rhandle)
|
|
|
|
|
+ } else {
|
|
|
|
|
+ Err(CsError::from_c(res))
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/// Finish with a connection to corosync.
|
|
|
|
|
+/// Takes a [Handle] as returned from [initialize]
|
|
|
|
|
+pub fn finalize(handle: Handle) -> Result<()> {
|
|
|
|
|
+ let res = unsafe { ffi::cmap_finalize(handle.cmap_handle) };
|
|
|
|
|
+ if res == ffi::CS_OK {
|
|
|
|
|
+ HANDLE_HASH.lock().unwrap().remove(&handle.cmap_handle);
|
|
|
|
|
+ Ok(())
|
|
|
|
|
+ } else {
|
|
|
|
|
+ Err(CsError::from_c(res))
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/// Return a file descriptor to use for poll/select on the CMAP handle.
|
|
|
|
|
+/// Takes a [Handle] as returned from [initialize],
|
|
|
|
|
+/// returns a C file descriptor as i32
|
|
|
|
|
+pub fn fd_get(handle: Handle) -> Result<i32> {
|
|
|
|
|
+ let c_fd: *mut c_int = &mut 0 as *mut _ as *mut c_int;
|
|
|
|
|
+ let res = unsafe { ffi::cmap_fd_get(handle.cmap_handle, c_fd) };
|
|
|
|
|
+ if res == ffi::CS_OK {
|
|
|
|
|
+ Ok(c_fd as i32)
|
|
|
|
|
+ } else {
|
|
|
|
|
+ Err(CsError::from_c(res))
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/// Dispatch any/all active CMAP callbacks.
|
|
|
|
|
+/// Takes a [Handle] as returned from [initialize],
|
|
|
|
|
+/// flags [DispatchFlags] tells it how many items to dispatch before returning
|
|
|
|
|
+pub fn dispatch(handle: Handle, flags: DispatchFlags) -> Result<()> {
|
|
|
|
|
+ let res = unsafe { ffi::cmap_dispatch(handle.cmap_handle, flags as u32) };
|
|
|
|
|
+ if res == ffi::CS_OK {
|
|
|
|
|
+ Ok(())
|
|
|
|
|
+ } else {
|
|
|
|
|
+ Err(CsError::from_c(res))
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/// Get the current 'context' value for this handle
|
|
|
|
|
+/// The context value is an arbitrary value that is always passed
|
|
|
|
|
+/// back to callbacks to help identify the source
|
|
|
|
|
+pub fn context_get(handle: Handle) -> Result<u64> {
|
|
|
|
|
+ let (res, context) = unsafe {
|
|
|
|
|
+ let mut context: u64 = 0;
|
|
|
|
|
+ let c_context: *mut c_void = &mut context as *mut _ as *mut c_void;
|
|
|
|
|
+ let r = ffi::cmap_context_get(handle.cmap_handle, c_context as *mut *const c_void);
|
|
|
|
|
+ (r, context)
|
|
|
|
|
+ };
|
|
|
|
|
+ if res == ffi::CS_OK {
|
|
|
|
|
+ Ok(context)
|
|
|
|
|
+ } else {
|
|
|
|
|
+ Err(CsError::from_c(res))
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/// Set the current 'context' value for this handle
|
|
|
|
|
+/// The context value is an arbitrary value that is always passed
|
|
|
|
|
+/// back to callbacks to help identify the source.
|
|
|
|
|
+/// Normally this is set in [initialize], but this allows it to be changed
|
|
|
|
|
+pub fn context_set(handle: Handle, context: u64) -> Result<()> {
|
|
|
|
|
+ let res = unsafe {
|
|
|
|
|
+ let c_context = context as *mut c_void;
|
|
|
|
|
+ ffi::cmap_context_set(handle.cmap_handle, c_context)
|
|
|
|
|
+ };
|
|
|
|
|
+ if res == ffi::CS_OK {
|
|
|
|
|
+ Ok(())
|
|
|
|
|
+ } else {
|
|
|
|
|
+ Err(CsError::from_c(res))
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/// The type of data returned from [get] or in a
|
|
|
|
|
+/// tracker callback or iterator, part of the [Data] struct
|
|
|
|
|
+#[derive(Clone, Copy, Debug, Eq, PartialEq, TryFromPrimitive)]
|
|
|
|
|
+#[repr(u32)]
|
|
|
|
|
+pub enum DataType {
|
|
|
|
|
+ Int8 = ffi::CMAP_VALUETYPE_INT8,
|
|
|
|
|
+ UInt8 = ffi::CMAP_VALUETYPE_UINT8,
|
|
|
|
|
+ Int16 = ffi::CMAP_VALUETYPE_INT16,
|
|
|
|
|
+ UInt16 = ffi::CMAP_VALUETYPE_UINT16,
|
|
|
|
|
+ Int32 = ffi::CMAP_VALUETYPE_INT32,
|
|
|
|
|
+ UInt32 = ffi::CMAP_VALUETYPE_UINT32,
|
|
|
|
|
+ Int64 = ffi::CMAP_VALUETYPE_INT64,
|
|
|
|
|
+ UInt64 = ffi::CMAP_VALUETYPE_UINT64,
|
|
|
|
|
+ Float = ffi::CMAP_VALUETYPE_FLOAT,
|
|
|
|
|
+ Double = ffi::CMAP_VALUETYPE_DOUBLE,
|
|
|
|
|
+ String = ffi::CMAP_VALUETYPE_STRING,
|
|
|
|
|
+ Binary = ffi::CMAP_VALUETYPE_BINARY,
|
|
|
|
|
+ Unknown = 999,
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+fn cmap_to_enum(cmap_type: u32) -> DataType {
|
|
|
|
|
+ match DataType::try_from(cmap_type) {
|
|
|
|
|
+ Ok(e) => e,
|
|
|
|
|
+ Err(_) => DataType::Unknown,
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/// Data returned from the cmap::get() call and tracker & iterators.
|
|
|
|
|
+/// Contains the data itself and the type of that data.
|
|
|
|
|
+pub enum Data {
|
|
|
|
|
+ Int8(i8),
|
|
|
|
|
+ UInt8(u8),
|
|
|
|
|
+ Int16(i16),
|
|
|
|
|
+ UInt16(u16),
|
|
|
|
|
+ Int32(i32),
|
|
|
|
|
+ UInt32(u32),
|
|
|
|
|
+ Int64(i64),
|
|
|
|
|
+ UInt64(u64),
|
|
|
|
|
+ Float(f32),
|
|
|
|
|
+ Double(f64),
|
|
|
|
|
+ String(String),
|
|
|
|
|
+ Binary(Vec<u8>),
|
|
|
|
|
+ Unknown,
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+impl fmt::Display for DataType {
|
|
|
|
|
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
|
+ match self {
|
|
|
|
|
+ DataType::Int8 => write!(f, "Int8"),
|
|
|
|
|
+ DataType::UInt8 => write!(f, "UInt8"),
|
|
|
|
|
+ DataType::Int16 => write!(f, "Int16"),
|
|
|
|
|
+ DataType::UInt16 => write!(f, "UInt16"),
|
|
|
|
|
+ DataType::Int32 => write!(f, "Int32"),
|
|
|
|
|
+ DataType::UInt32 => write!(f, "UInt32"),
|
|
|
|
|
+ DataType::Int64 => write!(f, "Int64"),
|
|
|
|
|
+ DataType::UInt64 => write!(f, "UInt64"),
|
|
|
|
|
+ DataType::Float => write!(f, "Float"),
|
|
|
|
|
+ DataType::Double => write!(f, "Double"),
|
|
|
|
|
+ DataType::String => write!(f, "String"),
|
|
|
|
|
+ DataType::Binary => write!(f, "Binary"),
|
|
|
|
|
+ DataType::Unknown => write!(f, "Unknown"),
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+impl fmt::Display for Data {
|
|
|
|
|
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
|
+ match self {
|
|
|
|
|
+ Data::Int8(v) => write!(f, "{} (Int8)", v),
|
|
|
|
|
+ Data::UInt8(v) => write!(f, "{} (UInt8)", v),
|
|
|
|
|
+ Data::Int16(v) => write!(f, "{} (Int16)", v),
|
|
|
|
|
+ Data::UInt16(v) => write!(f, "{} (UInt16)", v),
|
|
|
|
|
+ Data::Int32(v) => write!(f, "{} (Int32)", v),
|
|
|
|
|
+ Data::UInt32(v) => write!(f, "{} (UInt32)", v),
|
|
|
|
|
+ Data::Int64(v) => write!(f, "{} (Int64)", v),
|
|
|
|
|
+ Data::UInt64(v) => write!(f, "{} (UInt64)", v),
|
|
|
|
|
+ Data::Float(v) => write!(f, "{} (Float)", v),
|
|
|
|
|
+ Data::Double(v) => write!(f, "{} (Double)", v),
|
|
|
|
|
+ Data::String(v) => write!(f, "{} (String)", v),
|
|
|
|
|
+ Data::Binary(v) => write!(f, "{:?} (Binary)", v),
|
|
|
|
|
+ Data::Unknown => write!(f, "Unknown)"),
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const CMAP_KEYNAME_MAXLENGTH: usize = 255;
|
|
|
|
|
+fn string_to_cstring_validated(key: &str, maxlen: usize) -> Result<CString> {
|
|
|
|
|
+ if maxlen > 0 && key.chars().count() >= maxlen {
|
|
|
|
|
+ return Err(CsError::CsErrInvalidParam);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ match CString::new(key) {
|
|
|
|
|
+ Ok(n) => Ok(n),
|
|
|
|
|
+ Err(_) => Err(CsError::CsErrLibrary),
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+fn set_value(
|
|
|
|
|
+ handle: Handle,
|
|
|
|
|
+ key_name: &str,
|
|
|
|
|
+ datatype: DataType,
|
|
|
|
|
+ value: *mut c_void,
|
|
|
|
|
+ length: usize,
|
|
|
|
|
+) -> Result<()> {
|
|
|
|
|
+ let csname = string_to_cstring_validated(key_name, CMAP_KEYNAME_MAXLENGTH)?;
|
|
|
|
|
+ let res = unsafe {
|
|
|
|
|
+ ffi::cmap_set(
|
|
|
|
|
+ handle.cmap_handle,
|
|
|
|
|
+ csname.as_ptr(),
|
|
|
|
|
+ value,
|
|
|
|
|
+ length,
|
|
|
|
|
+ datatype as u32,
|
|
|
|
|
+ )
|
|
|
|
|
+ };
|
|
|
|
|
+ if res == ffi::CS_OK {
|
|
|
|
|
+ Ok(())
|
|
|
|
|
+ } else {
|
|
|
|
|
+ Err(CsError::from_c(res))
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// Returns type and size
|
|
|
|
|
+fn generic_to_cmap<T>(_value: T) -> (DataType, usize) {
|
|
|
|
|
+ match type_name::<T>() {
|
|
|
|
|
+ "u8" => (DataType::UInt8, 1),
|
|
|
|
|
+ "i8" => (DataType::Int8, 1),
|
|
|
|
|
+ "u16" => (DataType::UInt16, 2),
|
|
|
|
|
+ "i16" => (DataType::Int16, 2),
|
|
|
|
|
+ "u32" => (DataType::UInt32, 4),
|
|
|
|
|
+ "i32" => (DataType::Int32, 4),
|
|
|
|
|
+ "u64" => (DataType::UInt64, 4),
|
|
|
|
|
+ "f32" => (DataType::Float, 4),
|
|
|
|
|
+ "f64" => (DataType::Double, 8),
|
|
|
|
|
+ "&str" => (DataType::String, 0),
|
|
|
|
|
+ // Binary not currently supported here
|
|
|
|
|
+ _ => (DataType::Unknown, 0),
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+fn is_numeric_type(dtype: DataType) -> bool {
|
|
|
|
|
+ matches!(
|
|
|
|
|
+ dtype,
|
|
|
|
|
+ DataType::UInt8
|
|
|
|
|
+ | DataType::Int8
|
|
|
|
|
+ | DataType::UInt16
|
|
|
|
|
+ | DataType::Int16
|
|
|
|
|
+ | DataType::UInt32
|
|
|
|
|
+ | DataType::Int32
|
|
|
|
|
+ | DataType::UInt64
|
|
|
|
|
+ | DataType::Int64
|
|
|
|
|
+ | DataType::Float
|
|
|
|
|
+ | DataType::Double
|
|
|
|
|
+ )
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/// Function to set a generic numeric value
|
|
|
|
|
+/// This doesn't work for strings or binaries
|
|
|
|
|
+pub fn set_number<T: Copy>(handle: Handle, key_name: &str, value: T) -> Result<()> {
|
|
|
|
|
+ let (c_type, c_size) = generic_to_cmap(value);
|
|
|
|
|
+
|
|
|
|
|
+ if is_numeric_type(c_type) {
|
|
|
|
|
+ let mut tmp = value;
|
|
|
|
|
+ let c_value: *mut c_void = &mut tmp as *mut _ as *mut c_void;
|
|
|
|
|
+ set_value(handle, key_name, c_type, c_value as *mut c_void, c_size)
|
|
|
|
|
+ } else {
|
|
|
|
|
+ Err(CsError::CsErrNotSupported)
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+pub fn set_u8(handle: Handle, key_name: &str, value: u8) -> Result<()> {
|
|
|
|
|
+ let mut tmp = value;
|
|
|
|
|
+ let c_value: *mut c_void = &mut tmp as *mut _ as *mut c_void;
|
|
|
|
|
+ set_value(handle, key_name, DataType::UInt8, c_value as *mut c_void, 1)
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/// Sets an i8 value into cmap
|
|
|
|
|
+pub fn set_i8(handle: Handle, key_name: &str, value: i8) -> Result<()> {
|
|
|
|
|
+ let mut tmp = value;
|
|
|
|
|
+ let c_value: *mut c_void = &mut tmp as *mut _ as *mut c_void;
|
|
|
|
|
+ set_value(handle, key_name, DataType::Int8, c_value as *mut c_void, 1)
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/// Sets a u16 value into cmap
|
|
|
|
|
+pub fn set_u16(handle: Handle, key_name: &str, value: u16) -> Result<()> {
|
|
|
|
|
+ let mut tmp = value;
|
|
|
|
|
+ let c_value: *mut c_void = &mut tmp as *mut _ as *mut c_void;
|
|
|
|
|
+ set_value(
|
|
|
|
|
+ handle,
|
|
|
|
|
+ key_name,
|
|
|
|
|
+ DataType::UInt16,
|
|
|
|
|
+ c_value as *mut c_void,
|
|
|
|
|
+ 2,
|
|
|
|
|
+ )
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/// Sets an i16 value into cmap
|
|
|
|
|
+pub fn set_i16(handle: Handle, key_name: &str, value: i16) -> Result<()> {
|
|
|
|
|
+ let mut tmp = value;
|
|
|
|
|
+ let c_value: *mut c_void = &mut tmp as *mut _ as *mut c_void;
|
|
|
|
|
+ set_value(handle, key_name, DataType::Int16, c_value as *mut c_void, 2)
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/// Sets a u32 value into cmap
|
|
|
|
|
+pub fn set_u32(handle: Handle, key_name: &str, value: u32) -> Result<()> {
|
|
|
|
|
+ let mut tmp = value;
|
|
|
|
|
+ let c_value: *mut c_void = &mut tmp as *mut _ as *mut c_void;
|
|
|
|
|
+ set_value(handle, key_name, DataType::UInt32, c_value, 4)
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/// Sets an i32 value into cmap
|
|
|
|
|
+pub fn set_i132(handle: Handle, key_name: &str, value: i32) -> Result<()> {
|
|
|
|
|
+ let mut tmp = value;
|
|
|
|
|
+ let c_value: *mut c_void = &mut tmp as *mut _ as *mut c_void;
|
|
|
|
|
+ set_value(handle, key_name, DataType::Int32, c_value as *mut c_void, 4)
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/// Sets a u64 value into cmap
|
|
|
|
|
+pub fn set_u64(handle: Handle, key_name: &str, value: u64) -> Result<()> {
|
|
|
|
|
+ let mut tmp = value;
|
|
|
|
|
+ let c_value: *mut c_void = &mut tmp as *mut _ as *mut c_void;
|
|
|
|
|
+ set_value(
|
|
|
|
|
+ handle,
|
|
|
|
|
+ key_name,
|
|
|
|
|
+ DataType::UInt64,
|
|
|
|
|
+ c_value as *mut c_void,
|
|
|
|
|
+ 8,
|
|
|
|
|
+ )
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/// Sets an i64 value into cmap
|
|
|
|
|
+pub fn set_i164(handle: Handle, key_name: &str, value: i64) -> Result<()> {
|
|
|
|
|
+ let mut tmp = value;
|
|
|
|
|
+ let c_value: *mut c_void = &mut tmp as *mut _ as *mut c_void;
|
|
|
|
|
+ set_value(handle, key_name, DataType::Int64, c_value as *mut c_void, 8)
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/// Sets a string value into cmap
|
|
|
|
|
+pub fn set_string(handle: Handle, key_name: &str, value: &str) -> Result<()> {
|
|
|
|
|
+ let v_string = string_to_cstring_validated(value, 0)?;
|
|
|
|
|
+ set_value(
|
|
|
|
|
+ handle,
|
|
|
|
|
+ key_name,
|
|
|
|
|
+ DataType::String,
|
|
|
|
|
+ v_string.as_ptr() as *mut c_void,
|
|
|
|
|
+ value.chars().count(),
|
|
|
|
|
+ )
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/// Sets a binary value into cmap
|
|
|
|
|
+pub fn set_binary(handle: Handle, key_name: &str, value: &[u8]) -> Result<()> {
|
|
|
|
|
+ set_value(
|
|
|
|
|
+ handle,
|
|
|
|
|
+ key_name,
|
|
|
|
|
+ DataType::Binary,
|
|
|
|
|
+ value.as_ptr() as *mut c_void,
|
|
|
|
|
+ value.len(),
|
|
|
|
|
+ )
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/// Sets a [Data] type into cmap
|
|
|
|
|
+pub fn set(handle: Handle, key_name: &str, data: &Data) -> Result<()> {
|
|
|
|
|
+ let (datatype, datalen, c_value) = match data {
|
|
|
|
|
+ Data::Int8(v) => {
|
|
|
|
|
+ let mut tmp = *v;
|
|
|
|
|
+ let cv: *mut c_void = &mut tmp as *mut _ as *mut c_void;
|
|
|
|
|
+ (DataType::Int8, 1, cv)
|
|
|
|
|
+ }
|
|
|
|
|
+ Data::UInt8(v) => {
|
|
|
|
|
+ let mut tmp = *v;
|
|
|
|
|
+ let cv: *mut c_void = &mut tmp as *mut _ as *mut c_void;
|
|
|
|
|
+ (DataType::UInt8, 1, cv)
|
|
|
|
|
+ }
|
|
|
|
|
+ Data::Int16(v) => {
|
|
|
|
|
+ let mut tmp = *v;
|
|
|
|
|
+ let cv: *mut c_void = &mut tmp as *mut _ as *mut c_void;
|
|
|
|
|
+ (DataType::Int16, 2, cv)
|
|
|
|
|
+ }
|
|
|
|
|
+ Data::UInt16(v) => {
|
|
|
|
|
+ let mut tmp = *v;
|
|
|
|
|
+ let cv: *mut c_void = &mut tmp as *mut _ as *mut c_void;
|
|
|
|
|
+ (DataType::UInt8, 2, cv)
|
|
|
|
|
+ }
|
|
|
|
|
+ Data::Int32(v) => {
|
|
|
|
|
+ let mut tmp = *v;
|
|
|
|
|
+ let cv: *mut c_void = &mut tmp as *mut _ as *mut c_void;
|
|
|
|
|
+ (DataType::Int32, 4, cv)
|
|
|
|
|
+ }
|
|
|
|
|
+ Data::UInt32(v) => {
|
|
|
|
|
+ let mut tmp = *v;
|
|
|
|
|
+ let cv: *mut c_void = &mut tmp as *mut _ as *mut c_void;
|
|
|
|
|
+ (DataType::UInt32, 4, cv)
|
|
|
|
|
+ }
|
|
|
|
|
+ Data::Int64(v) => {
|
|
|
|
|
+ let mut tmp = *v;
|
|
|
|
|
+ let cv: *mut c_void = &mut tmp as *mut _ as *mut c_void;
|
|
|
|
|
+ (DataType::Int64, 8, cv)
|
|
|
|
|
+ }
|
|
|
|
|
+ Data::UInt64(v) => {
|
|
|
|
|
+ let mut tmp = *v;
|
|
|
|
|
+ let cv: *mut c_void = &mut tmp as *mut _ as *mut c_void;
|
|
|
|
|
+ (DataType::UInt64, 8, cv)
|
|
|
|
|
+ }
|
|
|
|
|
+ Data::Float(v) => {
|
|
|
|
|
+ let mut tmp = *v;
|
|
|
|
|
+ let cv: *mut c_void = &mut tmp as *mut _ as *mut c_void;
|
|
|
|
|
+ (DataType::Float, 4, cv)
|
|
|
|
|
+ }
|
|
|
|
|
+ Data::Double(v) => {
|
|
|
|
|
+ let mut tmp = *v;
|
|
|
|
|
+ let cv: *mut c_void = &mut tmp as *mut _ as *mut c_void;
|
|
|
|
|
+ (DataType::Double, 8, cv)
|
|
|
|
|
+ }
|
|
|
|
|
+ Data::String(v) => {
|
|
|
|
|
+ let cv = string_to_cstring_validated(v, 0)?;
|
|
|
|
|
+ // Can't let cv go out of scope
|
|
|
|
|
+ return set_value(
|
|
|
|
|
+ handle,
|
|
|
|
|
+ key_name,
|
|
|
|
|
+ DataType::String,
|
|
|
|
|
+ cv.as_ptr() as *mut c_void,
|
|
|
|
|
+ v.chars().count(),
|
|
|
|
|
+ );
|
|
|
|
|
+ }
|
|
|
|
|
+ Data::Binary(v) => {
|
|
|
|
|
+ // Vec doesn't return quite the right types.
|
|
|
|
|
+ return set_value(
|
|
|
|
|
+ handle,
|
|
|
|
|
+ key_name,
|
|
|
|
|
+ DataType::Binary,
|
|
|
|
|
+ v.as_ptr() as *mut c_void,
|
|
|
|
|
+ v.len(),
|
|
|
|
|
+ );
|
|
|
|
|
+ }
|
|
|
|
|
+ Data::Unknown => return Err(CsError::CsErrInvalidParam),
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ set_value(handle, key_name, datatype, c_value, datalen)
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// Local function to parse out values from the C mess
|
|
|
|
|
+// Assumes the c_value is complete. So cmap::get() will need to check the size
|
|
|
|
|
+// and re-get before calling us with a resized buffer
|
|
|
|
|
+fn c_to_data(value_size: usize, c_key_type: u32, c_value: *const u8) -> Result<Data> {
|
|
|
|
|
+ unsafe {
|
|
|
|
|
+ match cmap_to_enum(c_key_type) {
|
|
|
|
|
+ DataType::UInt8 => {
|
|
|
|
|
+ let mut ints = [0u8; 1];
|
|
|
|
|
+ copy_nonoverlapping(c_value as *mut u8, ints.as_mut_ptr() as *mut u8, value_size);
|
|
|
|
|
+ Ok(Data::UInt8(ints[0]))
|
|
|
|
|
+ }
|
|
|
|
|
+ DataType::Int8 => {
|
|
|
|
|
+ let mut ints = [0i8; 1];
|
|
|
|
|
+ copy_nonoverlapping(c_value as *mut u8, ints.as_mut_ptr() as *mut u8, value_size);
|
|
|
|
|
+ Ok(Data::Int8(ints[0]))
|
|
|
|
|
+ }
|
|
|
|
|
+ DataType::UInt16 => {
|
|
|
|
|
+ let mut ints = [0u16; 1];
|
|
|
|
|
+ copy_nonoverlapping(c_value as *mut u8, ints.as_mut_ptr() as *mut u8, value_size);
|
|
|
|
|
+ Ok(Data::UInt16(ints[0]))
|
|
|
|
|
+ }
|
|
|
|
|
+ DataType::Int16 => {
|
|
|
|
|
+ let mut ints = [0i16; 1];
|
|
|
|
|
+ copy_nonoverlapping(c_value as *mut u8, ints.as_mut_ptr() as *mut u8, value_size);
|
|
|
|
|
+ Ok(Data::Int16(ints[0]))
|
|
|
|
|
+ }
|
|
|
|
|
+ DataType::UInt32 => {
|
|
|
|
|
+ let mut ints = [0u32; 1];
|
|
|
|
|
+ copy_nonoverlapping(c_value as *mut u8, ints.as_mut_ptr() as *mut u8, value_size);
|
|
|
|
|
+ Ok(Data::UInt32(ints[0]))
|
|
|
|
|
+ }
|
|
|
|
|
+ DataType::Int32 => {
|
|
|
|
|
+ let mut ints = [0i32; 1];
|
|
|
|
|
+ copy_nonoverlapping(c_value as *mut u8, ints.as_mut_ptr() as *mut u8, value_size);
|
|
|
|
|
+ Ok(Data::Int32(ints[0]))
|
|
|
|
|
+ }
|
|
|
|
|
+ DataType::UInt64 => {
|
|
|
|
|
+ let mut ints = [0u64; 1];
|
|
|
|
|
+ copy_nonoverlapping(c_value as *mut u8, ints.as_mut_ptr() as *mut u8, value_size);
|
|
|
|
|
+ Ok(Data::UInt64(ints[0]))
|
|
|
|
|
+ }
|
|
|
|
|
+ DataType::Int64 => {
|
|
|
|
|
+ let mut ints = [0i64; 1];
|
|
|
|
|
+ copy_nonoverlapping(c_value as *mut u8, ints.as_mut_ptr() as *mut u8, value_size);
|
|
|
|
|
+ Ok(Data::Int64(ints[0]))
|
|
|
|
|
+ }
|
|
|
|
|
+ DataType::Float => {
|
|
|
|
|
+ let mut ints = [0f32; 1];
|
|
|
|
|
+ copy_nonoverlapping(c_value as *mut u8, ints.as_mut_ptr() as *mut u8, value_size);
|
|
|
|
|
+ Ok(Data::Float(ints[0]))
|
|
|
|
|
+ }
|
|
|
|
|
+ DataType::Double => {
|
|
|
|
|
+ let mut ints = [0f64; 1];
|
|
|
|
|
+ copy_nonoverlapping(c_value as *mut u8, ints.as_mut_ptr() as *mut u8, value_size);
|
|
|
|
|
+ Ok(Data::Double(ints[0]))
|
|
|
|
|
+ }
|
|
|
|
|
+ DataType::String => {
|
|
|
|
|
+ let mut ints = Vec::<u8>::new();
|
|
|
|
|
+ ints.resize(value_size, 0u8);
|
|
|
|
|
+ copy_nonoverlapping(c_value as *mut u8, ints.as_mut_ptr() as *mut u8, value_size);
|
|
|
|
|
+ // -1 here so CString doesn't see the NUL
|
|
|
|
|
+ let cs = match CString::new(&ints[0..value_size - 1_usize]) {
|
|
|
|
|
+ Ok(c1) => c1,
|
|
|
|
|
+ Err(_) => return Err(CsError::CsErrLibrary),
|
|
|
|
|
+ };
|
|
|
|
|
+ match cs.into_string() {
|
|
|
|
|
+ Ok(s) => Ok(Data::String(s)),
|
|
|
|
|
+ Err(_) => Err(CsError::CsErrLibrary),
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ DataType::Binary => {
|
|
|
|
|
+ let mut ints = Vec::<u8>::new();
|
|
|
|
|
+ ints.resize(value_size, 0u8);
|
|
|
|
|
+ copy_nonoverlapping(c_value as *mut u8, ints.as_mut_ptr() as *mut u8, value_size);
|
|
|
|
|
+ Ok(Data::Binary(ints))
|
|
|
|
|
+ }
|
|
|
|
|
+ DataType::Unknown => Ok(Data::Unknown),
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const INITIAL_SIZE: usize = 256;
|
|
|
|
|
+
|
|
|
|
|
+/// Get a value from cmap, returned as a [Data] struct, so could be anything
|
|
|
|
|
+pub fn get(handle: Handle, key_name: &str) -> Result<Data> {
|
|
|
|
|
+ let csname = string_to_cstring_validated(key_name, CMAP_KEYNAME_MAXLENGTH)?;
|
|
|
|
|
+ let mut value_size: usize = 16;
|
|
|
|
|
+ let mut c_key_type: u32 = 0;
|
|
|
|
|
+ let mut c_value = Vec::<u8>::new();
|
|
|
|
|
+
|
|
|
|
|
+ // First guess at a size for Strings and Binaries. Expand if needed
|
|
|
|
|
+ c_value.resize(INITIAL_SIZE, 0u8);
|
|
|
|
|
+
|
|
|
|
|
+ unsafe {
|
|
|
|
|
+ let res = ffi::cmap_get(
|
|
|
|
|
+ handle.cmap_handle,
|
|
|
|
|
+ csname.as_ptr(),
|
|
|
|
|
+ c_value.as_mut_ptr() as *mut c_void,
|
|
|
|
|
+ &mut value_size,
|
|
|
|
|
+ &mut c_key_type,
|
|
|
|
|
+ );
|
|
|
|
|
+ if res == ffi::CS_OK {
|
|
|
|
|
+ if value_size > INITIAL_SIZE {
|
|
|
|
|
+ // Need to try again with a bigger buffer
|
|
|
|
|
+ c_value.resize(value_size, 0u8);
|
|
|
|
|
+ let res2 = ffi::cmap_get(
|
|
|
|
|
+ handle.cmap_handle,
|
|
|
|
|
+ csname.as_ptr(),
|
|
|
|
|
+ c_value.as_mut_ptr() as *mut c_void,
|
|
|
|
|
+ &mut value_size,
|
|
|
|
|
+ &mut c_key_type,
|
|
|
|
|
+ );
|
|
|
|
|
+ if res2 != ffi::CS_OK {
|
|
|
|
|
+ return Err(CsError::from_c(res2));
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Convert to Rust type and return as a Data enum
|
|
|
|
|
+ c_to_data(value_size, c_key_type, c_value.as_ptr())
|
|
|
|
|
+ } else {
|
|
|
|
|
+ Err(CsError::from_c(res))
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/// increment the value in a cmap key (must be a numeric type)
|
|
|
|
|
+pub fn inc(handle: Handle, key_name: &str) -> Result<()> {
|
|
|
|
|
+ let csname = string_to_cstring_validated(key_name, CMAP_KEYNAME_MAXLENGTH)?;
|
|
|
|
|
+ let res = unsafe { ffi::cmap_inc(handle.cmap_handle, csname.as_ptr()) };
|
|
|
|
|
+ if res == ffi::CS_OK {
|
|
|
|
|
+ Ok(())
|
|
|
|
|
+ } else {
|
|
|
|
|
+ Err(CsError::from_c(res))
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/// decrement the value in a cmap key (must be a numeric type)
|
|
|
|
|
+pub fn dec(handle: Handle, key_name: &str) -> Result<()> {
|
|
|
|
|
+ let csname = string_to_cstring_validated(key_name, CMAP_KEYNAME_MAXLENGTH)?;
|
|
|
|
|
+ let res = unsafe { ffi::cmap_dec(handle.cmap_handle, csname.as_ptr()) };
|
|
|
|
|
+ if res == ffi::CS_OK {
|
|
|
|
|
+ Ok(())
|
|
|
|
|
+ } else {
|
|
|
|
|
+ Err(CsError::from_c(res))
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// Callback for CMAP notify events from corosync, convert params to Rust and pass on.
|
|
|
|
|
+extern "C" fn rust_notify_fn(
|
|
|
|
|
+ cmap_handle: ffi::cmap_handle_t,
|
|
|
|
|
+ cmap_track_handle: ffi::cmap_track_handle_t,
|
|
|
|
|
+ event: i32,
|
|
|
|
|
+ key_name: *const ::std::os::raw::c_char,
|
|
|
|
|
+ new_value: ffi::cmap_notify_value,
|
|
|
|
|
+ old_value: ffi::cmap_notify_value,
|
|
|
|
|
+ user_data: *mut ::std::os::raw::c_void,
|
|
|
|
|
+) {
|
|
|
|
|
+ // If cmap_handle doesn't match then throw away the callback.
|
|
|
|
|
+ if let Some(r_cmap_handle) = HANDLE_HASH.lock().unwrap().get(&cmap_handle) {
|
|
|
|
|
+ if let Some(h) = TRACKHANDLE_HASH.lock().unwrap().get(&cmap_track_handle) {
|
|
|
|
|
+ let r_keyname = match string_from_bytes(key_name, CMAP_KEYNAME_MAXLENGTH) {
|
|
|
|
|
+ Ok(s) => s,
|
|
|
|
|
+ Err(_) => return,
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ let r_old = match c_to_data(old_value.len, old_value.type_, old_value.data as *const u8)
|
|
|
|
|
+ {
|
|
|
|
|
+ Ok(v) => v,
|
|
|
|
|
+ Err(_) => return,
|
|
|
|
|
+ };
|
|
|
|
|
+ let r_new = match c_to_data(new_value.len, new_value.type_, new_value.data as *const u8)
|
|
|
|
|
+ {
|
|
|
|
|
+ Ok(v) => v,
|
|
|
|
|
+ Err(_) => return,
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ if let Some(cb) = h.notify_callback.notify_fn {
|
|
|
|
|
+ (cb)(
|
|
|
|
|
+ r_cmap_handle,
|
|
|
|
|
+ h,
|
|
|
|
|
+ TrackType { bits: event },
|
|
|
|
|
+ &r_keyname,
|
|
|
|
|
+ &r_old,
|
|
|
|
|
+ &r_new,
|
|
|
|
|
+ user_data as u64,
|
|
|
|
|
+ );
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/// Callback function called every time a tracker reports a change in a tracked value
|
|
|
|
|
+#[derive(Copy, Clone)]
|
|
|
|
|
+pub struct NotifyCallback {
|
|
|
|
|
+ pub notify_fn: Option<
|
|
|
|
|
+ fn(
|
|
|
|
|
+ handle: &Handle,
|
|
|
|
|
+ track_handle: &TrackHandle,
|
|
|
|
|
+ event: TrackType,
|
|
|
|
|
+ key_name: &str,
|
|
|
|
|
+ new_value: &Data,
|
|
|
|
|
+ old_value: &Data,
|
|
|
|
|
+ user_data: u64,
|
|
|
|
|
+ ),
|
|
|
|
|
+ >,
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/// Track changes in cmap values, multiple [TrackHandle]s per [Handle] are allowed
|
|
|
|
|
+pub fn track_add(
|
|
|
|
|
+ handle: Handle,
|
|
|
|
|
+ key_name: &str,
|
|
|
|
|
+ track_type: TrackType,
|
|
|
|
|
+ notify_callback: &NotifyCallback,
|
|
|
|
|
+ user_data: u64,
|
|
|
|
|
+) -> Result<TrackHandle> {
|
|
|
|
|
+ let c_name = string_to_cstring_validated(key_name, CMAP_KEYNAME_MAXLENGTH)?;
|
|
|
|
|
+ let mut c_trackhandle = 0u64;
|
|
|
|
|
+ let res = unsafe {
|
|
|
|
|
+ ffi::cmap_track_add(
|
|
|
|
|
+ handle.cmap_handle,
|
|
|
|
|
+ c_name.as_ptr(),
|
|
|
|
|
+ track_type.bits,
|
|
|
|
|
+ Some(rust_notify_fn),
|
|
|
|
|
+ user_data as *mut c_void,
|
|
|
|
|
+ &mut c_trackhandle,
|
|
|
|
|
+ )
|
|
|
|
|
+ };
|
|
|
|
|
+ if res == ffi::CS_OK {
|
|
|
|
|
+ let rhandle = TrackHandle {
|
|
|
|
|
+ track_handle: c_trackhandle,
|
|
|
|
|
+ notify_callback: *notify_callback,
|
|
|
|
|
+ };
|
|
|
|
|
+ TRACKHANDLE_HASH
|
|
|
|
|
+ .lock()
|
|
|
|
|
+ .unwrap()
|
|
|
|
|
+ .insert(c_trackhandle, rhandle);
|
|
|
|
|
+ Ok(rhandle)
|
|
|
|
|
+ } else {
|
|
|
|
|
+ Err(CsError::from_c(res))
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/// Remove a tracker frm this [Handle]
|
|
|
|
|
+pub fn track_delete(handle: Handle, track_handle: TrackHandle) -> Result<()> {
|
|
|
|
|
+ let res = unsafe { ffi::cmap_track_delete(handle.cmap_handle, track_handle.track_handle) };
|
|
|
|
|
+ if res == ffi::CS_OK {
|
|
|
|
|
+ TRACKHANDLE_HASH
|
|
|
|
|
+ .lock()
|
|
|
|
|
+ .unwrap()
|
|
|
|
|
+ .remove(&track_handle.track_handle);
|
|
|
|
|
+ Ok(())
|
|
|
|
|
+ } else {
|
|
|
|
|
+ Err(CsError::from_c(res))
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/// Create one of these to start iterating over cmap values.
|
|
|
|
|
+pub struct CmapIterStart {
|
|
|
|
|
+ iter_handle: u64,
|
|
|
|
|
+ cmap_handle: u64,
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+pub struct CmapIntoIter {
|
|
|
|
|
+ cmap_handle: u64,
|
|
|
|
|
+ iter_handle: u64,
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/// Value returned from the iterator. contains the key name and the [Data]
|
|
|
|
|
+pub struct CmapIter {
|
|
|
|
|
+ key_name: String,
|
|
|
|
|
+ data: Data,
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+impl CmapIter {
|
|
|
|
|
+ pub fn key_name(&self) -> &str {
|
|
|
|
|
+ &self.key_name
|
|
|
|
|
+ }
|
|
|
|
|
+ pub fn data(&self) -> &Data {
|
|
|
|
|
+ &self.data
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+impl fmt::Debug for CmapIter {
|
|
|
|
|
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
|
+ write!(f, "{}: {}", self.key_name, self.data)
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+impl Iterator for CmapIntoIter {
|
|
|
|
|
+ type Item = CmapIter;
|
|
|
|
|
+
|
|
|
|
|
+ fn next(&mut self) -> Option<CmapIter> {
|
|
|
|
|
+ let mut c_key_name = [0u8; CMAP_KEYNAME_MAXLENGTH + 1];
|
|
|
|
|
+ let mut c_value_len = 0usize;
|
|
|
|
|
+ let mut c_value_type = 0u32;
|
|
|
|
|
+ let res = unsafe {
|
|
|
|
|
+ ffi::cmap_iter_next(
|
|
|
|
|
+ self.cmap_handle,
|
|
|
|
|
+ self.iter_handle,
|
|
|
|
|
+ c_key_name.as_mut_ptr() as *mut c_char,
|
|
|
|
|
+ &mut c_value_len,
|
|
|
|
|
+ &mut c_value_type,
|
|
|
|
|
+ )
|
|
|
|
|
+ };
|
|
|
|
|
+ if res == ffi::CS_OK {
|
|
|
|
|
+ // Return the Data for this iteration
|
|
|
|
|
+ let mut c_value = Vec::<u8>::new();
|
|
|
|
|
+ c_value.resize(c_value_len, 0u8);
|
|
|
|
|
+ let res = unsafe {
|
|
|
|
|
+ ffi::cmap_get(
|
|
|
|
|
+ self.cmap_handle,
|
|
|
|
|
+ c_key_name.as_ptr() as *mut c_char,
|
|
|
|
|
+ c_value.as_mut_ptr() as *mut c_void,
|
|
|
|
|
+ &mut c_value_len,
|
|
|
|
|
+ &mut c_value_type,
|
|
|
|
|
+ )
|
|
|
|
|
+ };
|
|
|
|
|
+ if res == ffi::CS_OK {
|
|
|
|
|
+ match c_to_data(c_value_len, c_value_type, c_value.as_ptr()) {
|
|
|
|
|
+ Ok(d) => {
|
|
|
|
|
+ let r_keyname = match string_from_bytes(
|
|
|
|
|
+ c_key_name.as_ptr() as *mut c_char,
|
|
|
|
|
+ CMAP_KEYNAME_MAXLENGTH,
|
|
|
|
|
+ ) {
|
|
|
|
|
+ Ok(s) => s,
|
|
|
|
|
+ Err(_) => return None,
|
|
|
|
|
+ };
|
|
|
|
|
+ Some(CmapIter {
|
|
|
|
|
+ key_name: r_keyname,
|
|
|
|
|
+ data: d,
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+ Err(_) => None,
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // cmap_get returned error
|
|
|
|
|
+ None
|
|
|
|
|
+ }
|
|
|
|
|
+ } else if res == ffi::CS_ERR_NO_SECTIONS {
|
|
|
|
|
+ // End of list
|
|
|
|
|
+ unsafe {
|
|
|
|
|
+ // Yeah, we don't check this return code. There's nowhere to report it.
|
|
|
|
|
+ ffi::cmap_iter_finalize(self.cmap_handle, self.iter_handle)
|
|
|
|
|
+ };
|
|
|
|
|
+ None
|
|
|
|
|
+ } else {
|
|
|
|
|
+ None
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+impl CmapIterStart {
|
|
|
|
|
+ /// Create a new [CmapIterStart] object for iterating over a list of cmap keys
|
|
|
|
|
+ pub fn new(cmap_handle: Handle, prefix: &str) -> Result<CmapIterStart> {
|
|
|
|
|
+ let mut iter_handle: u64 = 0;
|
|
|
|
|
+ let res = unsafe {
|
|
|
|
|
+ let c_prefix = string_to_cstring_validated(prefix, CMAP_KEYNAME_MAXLENGTH)?;
|
|
|
|
|
+ ffi::cmap_iter_init(cmap_handle.cmap_handle, c_prefix.as_ptr(), &mut iter_handle)
|
|
|
|
|
+ };
|
|
|
|
|
+ if res == ffi::CS_OK {
|
|
|
|
|
+ Ok(CmapIterStart {
|
|
|
|
|
+ cmap_handle: cmap_handle.cmap_handle,
|
|
|
|
|
+ iter_handle,
|
|
|
|
|
+ })
|
|
|
|
|
+ } else {
|
|
|
|
|
+ Err(CsError::from_c(res))
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+impl IntoIterator for CmapIterStart {
|
|
|
|
|
+ type Item = CmapIter;
|
|
|
|
|
+ type IntoIter = CmapIntoIter;
|
|
|
|
|
+
|
|
|
|
|
+ fn into_iter(self) -> Self::IntoIter {
|
|
|
|
|
+ CmapIntoIter {
|
|
|
|
|
+ iter_handle: self.iter_handle,
|
|
|
|
|
+ cmap_handle: self.cmap_handle,
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+}
|