Skip to main content

rust_corosync/
quorum.rs

1// libquorum interface for Rust
2// Copyright (c) 2021 Red Hat, Inc.
3//
4// All rights reserved.
5//
6// Author: Christine Caulfield (ccaulfi@redhat.com)
7//
8
9#![allow(clippy::type_complexity)]
10#![allow(clippy::needless_range_loop)]
11#![allow(clippy::single_match)]
12
13// For the code generated by bindgen
14use crate::sys::quorum as ffi;
15
16use crate::{CsError, DispatchFlags, NodeId, Result, TrackFlags};
17use std::collections::HashMap;
18use std::os::raw::{c_int, c_void};
19use std::slice;
20use std::sync::Mutex;
21
22/// Data for model1 [initialize]
23#[derive(Copy, Clone)]
24pub enum ModelData {
25    ModelNone,
26    ModelV1(Model1Data),
27}
28
29/// Value returned from [initialize]. Indicates whether quorum is currently active on this cluster.
30pub enum QuorumType {
31    Free,
32    Set,
33}
34
35/// Flags for [initialize], none currently supported
36#[derive(Copy, Clone)]
37pub enum Model1Flags {
38    None,
39}
40
41/// RingId returned in quorum_notification_fn
42pub struct RingId {
43    pub nodeid: NodeId,
44    pub seq: u64,
45}
46
47// Used to convert a QUORUM handle into one of ours
48lazy_static! {
49    static ref HANDLE_HASH: Mutex<HashMap<u64, Handle>> = Mutex::new(HashMap::new());
50}
51
52fn list_to_vec(list_entries: u32, list: *const u32) -> Vec<NodeId> {
53    let mut r_member_list = Vec::<NodeId>::new();
54    let temp_members: &[u32] = unsafe { slice::from_raw_parts(list, list_entries as usize) };
55    for i in 0..list_entries as usize {
56        r_member_list.push(NodeId::from(temp_members[i]));
57    }
58    r_member_list
59}
60
61// Called from quorum callback function - munge params back to Rust from C
62extern "C" fn rust_quorum_notification_fn(
63    handle: ffi::quorum_handle_t,
64    quorate: u32,
65    ring_id: ffi::quorum_ring_id,
66    member_list_entries: u32,
67    member_list: *const u32,
68) {
69    if let Some(h) = HANDLE_HASH.lock().unwrap().get(&handle) {
70        let r_ring_id = RingId {
71            nodeid: NodeId::from(ring_id.nodeid),
72            seq: ring_id.seq,
73        };
74        let r_member_list = list_to_vec(member_list_entries, member_list);
75        let r_quorate = match quorate {
76            0 => false,
77            1 => true,
78            _ => false,
79        };
80        match &h.model_data {
81            ModelData::ModelV1(md) => {
82                if let Some(cb) = md.quorum_notification_fn {
83                    (cb)(h, r_quorate, r_ring_id, r_member_list);
84                }
85            }
86            _ => {}
87        }
88    }
89}
90
91extern "C" fn rust_nodelist_notification_fn(
92    handle: ffi::quorum_handle_t,
93    ring_id: ffi::quorum_ring_id,
94    member_list_entries: u32,
95    member_list: *const u32,
96    joined_list_entries: u32,
97    joined_list: *const u32,
98    left_list_entries: u32,
99    left_list: *const u32,
100) {
101    if let Some(h) = HANDLE_HASH.lock().unwrap().get(&handle) {
102        let r_ring_id = RingId {
103            nodeid: NodeId::from(ring_id.nodeid),
104            seq: ring_id.seq,
105        };
106
107        let r_member_list = list_to_vec(member_list_entries, member_list);
108        let r_joined_list = list_to_vec(joined_list_entries, joined_list);
109        let r_left_list = list_to_vec(left_list_entries, left_list);
110
111        match &h.model_data {
112            ModelData::ModelV1(md) => {
113                if let Some(cb) = md.nodelist_notification_fn {
114                    (cb)(h, r_ring_id, r_member_list, r_joined_list, r_left_list);
115                }
116            }
117            _ => {}
118        }
119    }
120}
121
122#[derive(Copy, Clone)]
123/// Data for model1 [initialize]
124pub struct Model1Data {
125    pub flags: Model1Flags,
126    pub quorum_notification_fn:
127        Option<fn(hande: &Handle, quorate: bool, ring_id: RingId, member_list: Vec<NodeId>)>,
128    pub nodelist_notification_fn: Option<
129        fn(
130            hande: &Handle,
131            ring_id: RingId,
132            member_list: Vec<NodeId>,
133            joined_list: Vec<NodeId>,
134            left_list: Vec<NodeId>,
135        ),
136    >,
137}
138
139/// A handle into the quorum library. Returned from [initialize] and needed for all other calls
140pub struct Handle {
141    quorum_handle: u64,
142    model_data: ModelData,
143    clone: bool,
144}
145
146impl Clone for Handle {
147    fn clone(&self) -> Handle {
148        Handle {
149            quorum_handle: self.quorum_handle,
150            model_data: self.model_data,
151            clone: true,
152        }
153    }
154}
155
156impl Drop for Handle {
157    fn drop(self: &mut Handle) {
158        if !self.clone {
159            let _e = finalize(self);
160        }
161    }
162}
163
164// Clones count as equivalent
165impl PartialEq for Handle {
166    fn eq(&self, other: &Handle) -> bool {
167        self.quorum_handle == other.quorum_handle
168    }
169}
170
171/// Initialize a connection to the quorum library. You must call this before doing anything
172/// else and use the passed back [Handle].
173/// Remember to free the handle using [finalize] when finished.
174pub fn initialize(model_data: &ModelData, context: u64) -> Result<(Handle, QuorumType)> {
175    let mut handle: ffi::quorum_handle_t = 0;
176    let mut quorum_type: u32 = 0;
177
178    let mut m = match model_data {
179        ModelData::ModelV1(_v1) => ffi::quorum_model_v1_data_t {
180            model: ffi::QUORUM_MODEL_V1,
181            quorum_notify_fn: Some(rust_quorum_notification_fn),
182            nodelist_notify_fn: Some(rust_nodelist_notification_fn),
183        },
184        // Only V1 supported. No point in doing legacy stuff in a new binding
185        _ => return Err(CsError::CsErrInvalidParam),
186    };
187
188    handle = unsafe {
189        let c_context: *mut c_void = &mut &context as *mut _ as *mut c_void;
190        let c_model: *mut ffi::quorum_model_data_t =
191            &mut m as *mut _ as *mut ffi::quorum_model_data_t;
192        let res = ffi::quorum_model_initialize(
193            &mut handle,
194            m.model,
195            c_model,
196            &mut quorum_type,
197            c_context,
198        );
199
200        if res == ffi::CS_OK {
201            handle
202        } else {
203            return Err(CsError::from_c(res));
204        }
205    };
206
207    let quorum_type = match quorum_type {
208        0 => QuorumType::Free,
209        1 => QuorumType::Set,
210        _ => QuorumType::Set,
211    };
212    let rhandle = Handle {
213        quorum_handle: handle,
214        model_data: *model_data,
215        clone: false,
216    };
217    HANDLE_HASH.lock().unwrap().insert(handle, rhandle.clone());
218    Ok((rhandle, quorum_type))
219}
220
221/// Finish with a connection to corosync
222pub fn finalize(handle: &Handle) -> Result<()> {
223    let res = unsafe { ffi::quorum_finalize(handle.quorum_handle) };
224    if res == ffi::CS_OK {
225        HANDLE_HASH.lock().unwrap().remove(&handle.quorum_handle);
226        Ok(())
227    } else {
228        Err(CsError::from_c(res))
229    }
230}
231
232// Not sure if an FD is the right thing to return here, but it will do for now.
233/// Return a file descriptor to use for poll/select on the QUORUM handle
234pub fn fd_get(handle: &Handle) -> Result<i32> {
235    let c_fd: *mut c_int = &mut 0 as *mut _ as *mut c_int;
236    let res = unsafe { ffi::quorum_fd_get(handle.quorum_handle, c_fd) };
237    if res == ffi::CS_OK {
238        Ok(unsafe { *c_fd })
239    } else {
240        Err(CsError::from_c(res))
241    }
242}
243
244/// Display any/all active QUORUM callbacks for this [Handle], see [DispatchFlags] for details
245pub fn dispatch(handle: &Handle, flags: DispatchFlags) -> Result<()> {
246    let res = unsafe { ffi::quorum_dispatch(handle.quorum_handle, flags as u32) };
247    if res == ffi::CS_OK {
248        Ok(())
249    } else {
250        Err(CsError::from_c(res))
251    }
252}
253
254/// Return the quorate status of the cluster
255pub fn getquorate(handle: &Handle) -> Result<bool> {
256    let c_quorate: *mut c_int = &mut 0 as *mut _ as *mut c_int;
257    let (res, r_quorate) = unsafe {
258        let res = ffi::quorum_getquorate(handle.quorum_handle, c_quorate);
259        let r_quorate: i32 = *c_quorate;
260        (res, r_quorate)
261    };
262    if res == ffi::CS_OK {
263        match r_quorate {
264            0 => Ok(false),
265            1 => Ok(true),
266            _ => Err(CsError::CsErrLibrary),
267        }
268    } else {
269        Err(CsError::from_c(res))
270    }
271}
272
273/// Track node and quorum changes
274pub fn trackstart(handle: &Handle, flags: TrackFlags) -> Result<()> {
275    let res = unsafe { ffi::quorum_trackstart(handle.quorum_handle, flags as u32) };
276    if res == ffi::CS_OK {
277        Ok(())
278    } else {
279        Err(CsError::from_c(res))
280    }
281}
282
283/// Stop tracking node and quorum changes
284pub fn trackstop(handle: &Handle) -> Result<()> {
285    let res = unsafe { ffi::quorum_trackstop(handle.quorum_handle) };
286    if res == ffi::CS_OK {
287        Ok(())
288    } else {
289        Err(CsError::from_c(res))
290    }
291}
292
293/// Get the current 'context' value for this handle.
294/// The context value is an arbitrary value that is always passed
295/// back to callbacks to help identify the source
296pub fn context_get(handle: &Handle) -> Result<u64> {
297    let (res, context) = unsafe {
298        let mut context: u64 = 0;
299        let c_context: *mut c_void = &mut context as *mut _ as *mut c_void;
300        let r = ffi::quorum_context_get(handle.quorum_handle, c_context as *mut *const c_void);
301        (r, context)
302    };
303    if res == ffi::CS_OK {
304        Ok(context)
305    } else {
306        Err(CsError::from_c(res))
307    }
308}
309
310/// Set the current 'context' value for this handle.
311/// The context value is an arbitrary value that is always passed
312/// back to callbacks to help identify the source.
313/// Normally this is set in [initialize], but this allows it to be changed
314pub fn context_set(handle: &Handle, context: u64) -> Result<()> {
315    let res = unsafe {
316        let c_context = context as *mut c_void;
317        ffi::quorum_context_set(handle.quorum_handle, c_context)
318    };
319    if res == ffi::CS_OK {
320        Ok(())
321    } else {
322        Err(CsError::from_c(res))
323    }
324}