diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-06-03 17:01:24 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-06-03 17:01:24 +0000 |
commit | 6dd3dfb79125cd02d02efbce435a6c82e5af92ef (patch) | |
tree | 45084fc83278586f6bbafcb935f92d53f71a6b03 /bindings/rust/src/lib.rs | |
parent | Initial commit. (diff) | |
download | corosync-6dd3dfb79125cd02d02efbce435a6c82e5af92ef.tar.xz corosync-6dd3dfb79125cd02d02efbce435a6c82e5af92ef.zip |
Adding upstream version 3.1.8.upstream/3.1.8upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'bindings/rust/src/lib.rs')
-rw-r--r-- | bindings/rust/src/lib.rs | 296 |
1 files changed, 296 insertions, 0 deletions
diff --git a/bindings/rust/src/lib.rs b/bindings/rust/src/lib.rs new file mode 100644 index 0000000..dbf34fc --- /dev/null +++ b/bindings/rust/src/lib.rs @@ -0,0 +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 <https://corosync.github.io/corosync/> +//! +//! 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<T> = ::std::result::Result<T, CsError>; + +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<u32> for NodeId { + fn from(id: u32) -> NodeId { + NodeId { id } + } +} + +impl From<NodeId> 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<String> { + 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), + } +} |