rust_corosync/
lib.rs

1//! This crate provides access to the corosync libraries cpg, cfg, cmap, quorum & votequorum
2//! from Rust. They are a fairly thin layer around the actual API calls but with Rust data types
3//! and iterators.
4//!
5//! Corosync is a low-level provider of cluster services for high-availability clusters,
6//! for more information about corosync see <https://corosync.github.io/corosync/>
7//!
8//! No more information about corosync itself will be provided here, it is expected that if
9//! you feel you need access to the Corosync API calls, you know what they do :)
10//!
11//! # Example
12//! ```
13//! extern crate rust_corosync as corosync;
14//! use corosync::cmap;
15//!
16//! fn main()
17//! {
18//!     // Open connection to corosync libcmap
19//!     let handle =
20//!     match cmap::initialize(cmap::Map::Icmap) {
21//!         Ok(h) => {
22//!             println!("cmap initialized.");
23//!             h
24//!         }
25//!         Err(e) => {
26//!             println!("Error in CMAP (Icmap) init: {}", e);
27//!             return;
28//!         }
29//!     };
30//!
31//!     // Set a numeric value (this is a generic fn)
32//!     match cmap::set_number(handle, "test.test_uint32", 456)
33//!     {
34//!         Ok(_) => {}
35//!         Err(e) => {
36//!             println!("Error in CMAP set_u32: {}", e);
37//!             return;
38//!         }
39//!     };
40//!
41//!     // Get a value - this will be a Data struct
42//!     match cmap::get(handle, "test.test_uint32")
43//!     {
44//!         Ok(v) => {
45//!             println!("GOT value {}", v);
46//!         }
47//!         Err(e) => {
48//!             println!("Error in CMAP get: {}", e);
49//!             return;
50//!         }
51//!     };
52//!
53//!     // Use an iterator
54//!     match cmap::CmapIterStart::new(handle, "totem.") {
55//!         Ok(cmap_iter) => {
56//!             for i in cmap_iter {
57//!                 println!("ITER: {:?}", i);
58//!             }
59//!             println!("");
60//!         }
61//!         Err(e) => {
62//!             println!("Error in CMAP iter start: {}", e);
63//!         }
64//!     }
65//!
66//!     // Close this connection
67//!     match cmap::finalize(handle)
68//!     {
69//!         Ok(_) => {}
70//!         Err(e) => {
71//!             println!("Error in CMAP get: {}", e);
72//!             return;
73//!         }
74//!     };
75//! }
76
77#[macro_use]
78extern crate lazy_static;
79#[macro_use]
80extern crate bitflags;
81
82/// cfg is the internal configuration and information library for corosync, it is
83/// mainly used by internal tools but may also contain API calls useful to some applications
84/// that need detailed information about or control of the operation of corosync and the cluster.
85pub mod cfg;
86/// cmap is the internal 'database' of corosync - though it is NOT replicated. Mostly it contains
87/// a copy of the corosync.conf file and information about the running state of the daemon.
88/// The cmap API provides two 'maps'. Icmap, which is as above, and Stats, which contains very detailed
89/// statistics on the running system, this includes network and IPC calls.
90pub mod cmap;
91/// cpg is the Control Process Groups subsystem of corosync and is usually used for sending
92/// messages around the cluster. All processes using CPG belong to a named group (whose members
93/// they can query) and all messages are sent with delivery guarantees.
94pub mod cpg;
95/// Quorum provides basic information about the quorate state of the cluster with callbacks
96/// when nodelists change.
97pub mod quorum;
98///votequorum is the main quorum provider for corosync, using this API, users can query the state
99/// of nodes in the cluster, request callbacks when the nodelists change, and set up a quorum device.
100pub mod votequorum;
101
102mod sys;
103
104use num_enum::TryFromPrimitive;
105use std::convert::TryFrom;
106use std::error::Error;
107use std::ffi::CString;
108use std::fmt;
109use std::ptr::copy_nonoverlapping;
110
111// This needs to be kept up-to-date!
112/// Error codes returned from the corosync libraries
113#[derive(Debug, Eq, PartialEq, Copy, Clone, TryFromPrimitive)]
114#[repr(u32)]
115pub enum CsError {
116    CsOk = 1,
117    CsErrLibrary = 2,
118    CsErrVersion = 3,
119    CsErrInit = 4,
120    CsErrTimeout = 5,
121    CsErrTryAgain = 6,
122    CsErrInvalidParam = 7,
123    CsErrNoMemory = 8,
124    CsErrBadHandle = 9,
125    CsErrBusy = 10,
126    CsErrAccess = 11,
127    CsErrNotExist = 12,
128    CsErrNameTooLong = 13,
129    CsErrExist = 14,
130    CsErrNoSpace = 15,
131    CsErrInterrupt = 16,
132    CsErrNameNotFound = 17,
133    CsErrNoResources = 18,
134    CsErrNotSupported = 19,
135    CsErrBadOperation = 20,
136    CsErrFailedOperation = 21,
137    CsErrMessageError = 22,
138    CsErrQueueFull = 23,
139    CsErrQueueNotAvailable = 24,
140    CsErrBadFlags = 25,
141    CsErrTooBig = 26,
142    CsErrNoSection = 27,
143    CsErrContextNotFound = 28,
144    CsErrTooManyGroups = 30,
145    CsErrSecurity = 100,
146    #[num_enum(default)]
147    CsErrRustCompat = 998, // Set if we get a unknown return from corosync
148    CsErrRustString = 999, // Set if we get a string conversion error
149}
150
151/// Result type returned from most corosync library calls.
152/// Contains a [CsError] and possibly other data as required
153pub type Result<T> = ::std::result::Result<T, CsError>;
154
155impl fmt::Display for CsError {
156    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
157        match self {
158            CsError::CsOk => write!(f, "OK"),
159            CsError::CsErrLibrary => write!(f, "ErrLibrary"),
160            CsError::CsErrVersion => write!(f, "ErrVersion"),
161            CsError::CsErrInit => write!(f, "ErrInit"),
162            CsError::CsErrTimeout => write!(f, "ErrTimeout"),
163            CsError::CsErrTryAgain => write!(f, "ErrTryAgain"),
164            CsError::CsErrInvalidParam => write!(f, "ErrInvalidParam"),
165            CsError::CsErrNoMemory => write!(f, "ErrNoMemory"),
166            CsError::CsErrBadHandle => write!(f, "ErrbadHandle"),
167            CsError::CsErrBusy => write!(f, "ErrBusy"),
168            CsError::CsErrAccess => write!(f, "ErrAccess"),
169            CsError::CsErrNotExist => write!(f, "ErrNotExist"),
170            CsError::CsErrNameTooLong => write!(f, "ErrNameTooLong"),
171            CsError::CsErrExist => write!(f, "ErrExist"),
172            CsError::CsErrNoSpace => write!(f, "ErrNoSpace"),
173            CsError::CsErrInterrupt => write!(f, "ErrInterrupt"),
174            CsError::CsErrNameNotFound => write!(f, "ErrNameNotFound"),
175            CsError::CsErrNoResources => write!(f, "ErrNoResources"),
176            CsError::CsErrNotSupported => write!(f, "ErrNotSupported"),
177            CsError::CsErrBadOperation => write!(f, "ErrBadOperation"),
178            CsError::CsErrFailedOperation => write!(f, "ErrFailedOperation"),
179            CsError::CsErrMessageError => write!(f, "ErrMEssageError"),
180            CsError::CsErrQueueFull => write!(f, "ErrQueueFull"),
181            CsError::CsErrQueueNotAvailable => write!(f, "ErrQueueNotAvailable"),
182            CsError::CsErrBadFlags => write!(f, "ErrBadFlags"),
183            CsError::CsErrTooBig => write!(f, "ErrTooBig"),
184            CsError::CsErrNoSection => write!(f, "ErrNoSection"),
185            CsError::CsErrContextNotFound => write!(f, "ErrContextNotFound"),
186            CsError::CsErrTooManyGroups => write!(f, "ErrTooManyGroups"),
187            CsError::CsErrSecurity => write!(f, "ErrSecurity"),
188            CsError::CsErrRustCompat => write!(f, "ErrRustCompat"),
189            CsError::CsErrRustString => write!(f, "ErrRustString"),
190        }
191    }
192}
193
194impl Error for CsError {}
195
196// This is dependant on the num_enum crate, converts a C cs_error_t into the Rust enum
197// There seems to be some debate as to whether this should be part of the language:
198// https://internals.rust-lang.org/t/pre-rfc-enum-from-integer/6348/25
199impl CsError {
200    fn from_c(cserr: u32) -> CsError {
201        match CsError::try_from(cserr) {
202            Ok(e) => e,
203            Err(_) => CsError::CsErrRustCompat,
204        }
205    }
206}
207
208/// Flags to use with dispatch functions, eg [cpg::dispatch]
209/// One will dispatch a single callback (blocking) and return.
210/// All will loop trying to dispatch all possible callbacks.
211/// Blocking is like All but will block between callbacks.
212/// OneNonBlocking will dispatch a single callback only if one is available,
213/// otherwise it will return even if no callback is available.
214#[derive(Copy, Clone)]
215// The numbers match the C enum, of course.
216pub enum DispatchFlags {
217    One = 1,
218    All = 2,
219    Blocking = 3,
220    OneNonblocking = 4,
221}
222
223/// Flags to use with (most) tracking API calls
224#[derive(Copy, Clone)]
225// Same here
226pub enum TrackFlags {
227    Current = 1,
228    Changes = 2,
229    ChangesOnly = 4,
230}
231
232/// A corosync nodeid
233#[derive(Copy, Clone, Debug, PartialEq, Eq)]
234pub struct NodeId {
235    id: u32,
236}
237
238impl fmt::Display for NodeId {
239    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
240        write!(f, "{}", self.id)
241    }
242}
243
244// Conversion from a NodeId to and from u32
245impl From<u32> for NodeId {
246    fn from(id: u32) -> NodeId {
247        NodeId { id }
248    }
249}
250
251impl From<NodeId> for u32 {
252    fn from(nodeid: NodeId) -> u32 {
253        nodeid.id
254    }
255}
256
257// General internal routine to copy bytes from a C array into a Rust String
258fn string_from_bytes(bytes: *const ::std::os::raw::c_char, max_length: usize) -> Result<String> {
259    let mut newbytes = Vec::<u8>::new();
260    newbytes.resize(max_length, 0u8);
261
262    // Get length of the string in old-fashioned style
263    let mut length: usize = 0;
264    let mut count = 0;
265    let mut tmpbytes = bytes;
266    while count < max_length || length == 0 {
267        if unsafe { *tmpbytes } == 0 && length == 0 {
268            length = count;
269            break;
270        }
271        count += 1;
272        tmpbytes = unsafe { tmpbytes.offset(1) }
273    }
274
275    // Cope with an empty string
276    if length == 0 {
277        return Ok(String::new());
278    }
279
280    unsafe {
281        // We need to fully copy it, not shallow copy it.
282        // Messy casting on both parts of the copy here to get it to work on both signed
283        // and unsigned char machines
284        copy_nonoverlapping(bytes as *mut i8, newbytes.as_mut_ptr() as *mut i8, length);
285    }
286
287    let cs = match CString::new(&newbytes[0..length]) {
288        Ok(c1) => c1,
289        Err(_) => return Err(CsError::CsErrRustString),
290    };
291
292    // This is just to convert the error type
293    match cs.into_string() {
294        Ok(s) => Ok(s),
295        Err(_) => Err(CsError::CsErrRustString),
296    }
297}