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}