diff --git a/bindings/rust/src/cmap.rs b/bindings/rust/src/cmap.rs index e0dd3d4c..454fbee2 100644 --- a/bindings/rust/src/cmap.rs +++ b/bindings/rust/src/cmap.rs @@ -1,898 +1,894 @@ // 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> = Mutex::new(HashMap::new()); static ref HANDLE_HASH: Mutex> = 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 { 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 { 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 { 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), 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, "{v} (Int8)"), Data::UInt8(v) => write!(f, "{v} (UInt8)"), Data::Int16(v) => write!(f, "{v} (Int16)"), Data::UInt16(v) => write!(f, "{v} (UInt16)"), Data::Int32(v) => write!(f, "{v} (Int32)"), Data::UInt32(v) => write!(f, "{v} (UInt32)"), Data::Int64(v) => write!(f, "{v} (Int64)"), Data::UInt64(v) => write!(f, "{v} (UInt64)"), Data::Float(v) => write!(f, "{v} (Float)"), Data::Double(v) => write!(f, "{v} (Double)"), Data::String(v) => write!(f, "{v} (String)"), Data::Binary(v) => write!(f, "{v:?} (Binary)"), Data::Unknown => write!(f, "Unknown)"), } } } const CMAP_KEYNAME_MAXLENGTH: usize = 255; fn string_to_cstring_validated(key: &str, maxlen: usize) -> Result { 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(_value: T) -> (DataType, usize) { match type_name::() { "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(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 { 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(), 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::::new(); - ints.resize(value_size, 0u8); + let mut ints = vec![0u8; value_size]; copy_nonoverlapping(c_value as *mut u8, ints.as_mut_ptr(), 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::::new(); - ints.resize(value_size, 0u8); + let mut ints = vec![0u8; value_size]; copy_nonoverlapping(c_value as *mut u8, ints.as_mut_ptr(), 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 { 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::::new(); // First guess at a size for Strings and Binaries. Expand if needed - c_value.resize(INITIAL_SIZE, 0u8); + let mut c_value = vec![0u8; INITIAL_SIZE]; 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 { 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 { 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::::new(); - c_value.resize(c_value_len, 0u8); + let mut c_value = vec![0u8; c_value_len]; 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 { 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, } } } diff --git a/bindings/rust/src/lib.rs b/bindings/rust/src/lib.rs index cd4326e7..dbf34fc1 100644 --- a/bindings/rust/src/lib.rs +++ b/bindings/rust/src/lib.rs @@ -1,297 +1,296 @@ //! This crate provides access to the corosync libraries cpg, cfg, cmap, quorum & votequorum //! from Rust. They are a fairly thin layer around the actual API calls but with Rust data types //! and iterators. //! //! Corosync is a low-level provider of cluster services for high-availability clusters, //! for more information about corosync see //! //! No more information about corosync itself will be provided here, it is expected that if //! you feel you need access to the Corosync API calls, you know what they do :) //! //! # Example //! ``` //! extern crate rust_corosync as corosync; //! use corosync::cmap; //! //! fn main() //! { //! // Open connection to corosync libcmap //! let handle = //! match cmap::initialize(cmap::Map::Icmap) { //! Ok(h) => { //! println!("cmap initialized."); //! h //! } //! Err(e) => { //! println!("Error in CMAP (Icmap) init: {}", e); //! return; //! } //! }; //! //! // Set a numeric value (this is a generic fn) //! match cmap::set_number(handle, "test.test_uint32", 456) //! { //! Ok(_) => {} //! Err(e) => { //! println!("Error in CMAP set_u32: {}", e); //! return; //! } //! }; //! //! // Get a value - this will be a Data struct //! match cmap::get(handle, "test.test_uint32") //! { //! Ok(v) => { //! println!("GOT value {}", v); //! } //! Err(e) => { //! println!("Error in CMAP get: {}", e); //! return; //! } //! }; //! //! // Use an iterator //! match cmap::CmapIterStart::new(handle, "totem.") { //! Ok(cmap_iter) => { //! for i in cmap_iter { //! println!("ITER: {:?}", i); //! } //! println!(""); //! } //! Err(e) => { //! println!("Error in CMAP iter start: {}", e); //! } //! } //! //! // Close this connection //! match cmap::finalize(handle) //! { //! Ok(_) => {} //! Err(e) => { //! println!("Error in CMAP get: {}", e); //! return; //! } //! }; //! } #[macro_use] extern crate lazy_static; #[macro_use] extern crate bitflags; /// cfg is the internal configuration and information library for corosync, it is /// mainly used by internal tools but may also contain API calls useful to some applications /// that need detailed information about or control of the operation of corosync and the cluster. pub mod cfg; /// cmap is the internal 'database' of corosync - though it is NOT replicated. Mostly it contains /// a copy of the corosync.conf file and information about the running state of the daemon. /// The cmap API provides two 'maps'. Icmap, which is as above, and Stats, which contains very detailed /// statistics on the running system, this includes network and IPC calls. pub mod cmap; /// cpg is the Control Process Groups subsystem of corosync and is usually used for sending /// messages around the cluster. All processes using CPG belong to a named group (whose members /// they can query) and all messages are sent with delivery guarantees. pub mod cpg; /// Quorum provides basic information about the quorate state of the cluster with callbacks /// when nodelists change. pub mod quorum; ///votequorum is the main quorum provider for corosync, using this API, users can query the state /// of nodes in the cluster, request callbacks when the nodelists change, and set up a quorum device. pub mod votequorum; mod sys; use num_enum::TryFromPrimitive; use std::convert::TryFrom; use std::error::Error; use std::ffi::CString; use std::fmt; use std::ptr::copy_nonoverlapping; // This needs to be kept up-to-date! /// Error codes returned from the corosync libraries #[derive(Debug, Eq, PartialEq, Copy, Clone, TryFromPrimitive)] #[repr(u32)] pub enum CsError { CsOk = 1, CsErrLibrary = 2, CsErrVersion = 3, CsErrInit = 4, CsErrTimeout = 5, CsErrTryAgain = 6, CsErrInvalidParam = 7, CsErrNoMemory = 8, CsErrBadHandle = 9, CsErrBusy = 10, CsErrAccess = 11, CsErrNotExist = 12, CsErrNameTooLong = 13, CsErrExist = 14, CsErrNoSpace = 15, CsErrInterrupt = 16, CsErrNameNotFound = 17, CsErrNoResources = 18, CsErrNotSupported = 19, CsErrBadOperation = 20, CsErrFailedOperation = 21, CsErrMessageError = 22, CsErrQueueFull = 23, CsErrQueueNotAvailable = 24, CsErrBadFlags = 25, CsErrTooBig = 26, CsErrNoSection = 27, CsErrContextNotFound = 28, CsErrTooManyGroups = 30, CsErrSecurity = 100, #[num_enum(default)] CsErrRustCompat = 998, // Set if we get a unknown return from corosync CsErrRustString = 999, // Set if we get a string conversion error } /// Result type returned from most corosync library calls. /// Contains a [CsError] and possibly other data as required pub type Result = ::std::result::Result; impl fmt::Display for CsError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { CsError::CsOk => write!(f, "OK"), CsError::CsErrLibrary => write!(f, "ErrLibrary"), CsError::CsErrVersion => write!(f, "ErrVersion"), CsError::CsErrInit => write!(f, "ErrInit"), CsError::CsErrTimeout => write!(f, "ErrTimeout"), CsError::CsErrTryAgain => write!(f, "ErrTryAgain"), CsError::CsErrInvalidParam => write!(f, "ErrInvalidParam"), CsError::CsErrNoMemory => write!(f, "ErrNoMemory"), CsError::CsErrBadHandle => write!(f, "ErrbadHandle"), CsError::CsErrBusy => write!(f, "ErrBusy"), CsError::CsErrAccess => write!(f, "ErrAccess"), CsError::CsErrNotExist => write!(f, "ErrNotExist"), CsError::CsErrNameTooLong => write!(f, "ErrNameTooLong"), CsError::CsErrExist => write!(f, "ErrExist"), CsError::CsErrNoSpace => write!(f, "ErrNoSpace"), CsError::CsErrInterrupt => write!(f, "ErrInterrupt"), CsError::CsErrNameNotFound => write!(f, "ErrNameNotFound"), CsError::CsErrNoResources => write!(f, "ErrNoResources"), CsError::CsErrNotSupported => write!(f, "ErrNotSupported"), CsError::CsErrBadOperation => write!(f, "ErrBadOperation"), CsError::CsErrFailedOperation => write!(f, "ErrFailedOperation"), CsError::CsErrMessageError => write!(f, "ErrMEssageError"), CsError::CsErrQueueFull => write!(f, "ErrQueueFull"), CsError::CsErrQueueNotAvailable => write!(f, "ErrQueueNotAvailable"), CsError::CsErrBadFlags => write!(f, "ErrBadFlags"), CsError::CsErrTooBig => write!(f, "ErrTooBig"), CsError::CsErrNoSection => write!(f, "ErrNoSection"), CsError::CsErrContextNotFound => write!(f, "ErrContextNotFound"), CsError::CsErrTooManyGroups => write!(f, "ErrTooManyGroups"), CsError::CsErrSecurity => write!(f, "ErrSecurity"), CsError::CsErrRustCompat => write!(f, "ErrRustCompat"), CsError::CsErrRustString => write!(f, "ErrRustString"), } } } impl Error for CsError {} // This is dependant on the num_enum crate, converts a C cs_error_t into the Rust enum // There seems to be some debate as to whether this should be part of the language: // https://internals.rust-lang.org/t/pre-rfc-enum-from-integer/6348/25 impl CsError { fn from_c(cserr: u32) -> CsError { match CsError::try_from(cserr) { Ok(e) => e, Err(_) => CsError::CsErrRustCompat, } } } /// Flags to use with dispatch functions, eg [cpg::dispatch] /// One will dispatch a single callback (blocking) and return. /// All will loop trying to dispatch all possible callbacks. /// Blocking is like All but will block between callbacks. /// OneNonBlocking will dispatch a single callback only if one is available, /// otherwise it will return even if no callback is available. #[derive(Copy, Clone)] // The numbers match the C enum, of course. pub enum DispatchFlags { One = 1, All = 2, Blocking = 3, OneNonblocking = 4, } /// Flags to use with (most) tracking API calls #[derive(Copy, Clone)] // Same here pub enum TrackFlags { Current = 1, Changes = 2, ChangesOnly = 4, } /// A corosync nodeid #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub struct NodeId { id: u32, } impl fmt::Display for NodeId { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.id) } } // Conversion from a NodeId to and from u32 impl From for NodeId { fn from(id: u32) -> NodeId { NodeId { id } } } impl From for u32 { fn from(nodeid: NodeId) -> u32 { nodeid.id } } // General internal routine to copy bytes from a C array into a Rust String fn string_from_bytes(bytes: *const ::std::os::raw::c_char, max_length: usize) -> Result { - let mut newbytes = Vec::::new(); - newbytes.resize(max_length, 0u8); + let mut newbytes = vec![0u8; max_length]; // Get length of the string in old-fashioned style let mut length: usize = 0; let mut count = 0; let mut tmpbytes = bytes; while count < max_length || length == 0 { if unsafe { *tmpbytes } == 0 && length == 0 { length = count; break; } count += 1; tmpbytes = unsafe { tmpbytes.offset(1) } } // Cope with an empty string if length == 0 { return Ok(String::new()); } unsafe { // We need to fully copy it, not shallow copy it. // Messy casting on both parts of the copy here to get it to work on both signed // and unsigned char machines copy_nonoverlapping(bytes as *mut i8, newbytes.as_mut_ptr() as *mut i8, length); } let cs = match CString::new(&newbytes[0..length]) { Ok(c1) => c1, Err(_) => return Err(CsError::CsErrRustString), }; // This is just to convert the error type match cs.into_string() { Ok(s) => Ok(s), Err(_) => Err(CsError::CsErrRustString), } }