//! 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![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), } }