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
140#[derive(Copy, Clone)]
141pub struct Handle {
142    quorum_handle: u64,
143    model_data: ModelData,
144}
145
146/// Initialize a connection to the quorum library. You must call this before doing anything
147/// else and use the passed back [Handle].
148/// Remember to free the handle using [finalize] when finished.
149pub fn initialize(model_data: &ModelData, context: u64) -> Result<(Handle, QuorumType)> {
150    let mut handle: ffi::quorum_handle_t = 0;
151    let mut quorum_type: u32 = 0;
152
153    let mut m = match model_data {
154        ModelData::ModelV1(_v1) => ffi::quorum_model_v1_data_t {
155            model: ffi::QUORUM_MODEL_V1,
156            quorum_notify_fn: Some(rust_quorum_notification_fn),
157            nodelist_notify_fn: Some(rust_nodelist_notification_fn),
158        },
159        // Only V1 supported. No point in doing legacy stuff in a new binding
160        _ => return Err(CsError::CsErrInvalidParam),
161    };
162
163    handle = unsafe {
164        let c_context: *mut c_void = &mut &context as *mut _ as *mut c_void;
165        let c_model: *mut ffi::quorum_model_data_t =
166            &mut m as *mut _ as *mut ffi::quorum_model_data_t;
167        let res = ffi::quorum_model_initialize(
168            &mut handle,
169            m.model,
170            c_model,
171            &mut quorum_type,
172            c_context,
173        );
174
175        if res == ffi::CS_OK {
176            handle
177        } else {
178            return Err(CsError::from_c(res));
179        }
180    };
181
182    let quorum_type = match quorum_type {
183        0 => QuorumType::Free,
184        1 => QuorumType::Set,
185        _ => QuorumType::Set,
186    };
187    let rhandle = Handle {
188        quorum_handle: handle,
189        model_data: *model_data,
190    };
191    HANDLE_HASH.lock().unwrap().insert(handle, rhandle);
192    Ok((rhandle, quorum_type))
193}
194
195/// Finish with a connection to corosync
196pub fn finalize(handle: Handle) -> Result<()> {
197    let res = unsafe { ffi::quorum_finalize(handle.quorum_handle) };
198    if res == ffi::CS_OK {
199        HANDLE_HASH.lock().unwrap().remove(&handle.quorum_handle);
200        Ok(())
201    } else {
202        Err(CsError::from_c(res))
203    }
204}
205
206// Not sure if an FD is the right thing to return here, but it will do for now.
207/// Return a file descriptor to use for poll/select on the QUORUM handle
208pub fn fd_get(handle: Handle) -> Result<i32> {
209    let c_fd: *mut c_int = &mut 0 as *mut _ as *mut c_int;
210    let res = unsafe { ffi::quorum_fd_get(handle.quorum_handle, c_fd) };
211    if res == ffi::CS_OK {
212        Ok(c_fd as i32)
213    } else {
214        Err(CsError::from_c(res))
215    }
216}
217
218/// Display any/all active QUORUM callbacks for this [Handle], see [DispatchFlags] for details
219pub fn dispatch(handle: Handle, flags: DispatchFlags) -> Result<()> {
220    let res = unsafe { ffi::quorum_dispatch(handle.quorum_handle, flags as u32) };
221    if res == ffi::CS_OK {
222        Ok(())
223    } else {
224        Err(CsError::from_c(res))
225    }
226}
227
228/// Return the quorate status of the cluster
229pub fn getquorate(handle: Handle) -> Result<bool> {
230    let c_quorate: *mut c_int = &mut 0 as *mut _ as *mut c_int;
231    let (res, r_quorate) = unsafe {
232        let res = ffi::quorum_getquorate(handle.quorum_handle, c_quorate);
233        let r_quorate: i32 = *c_quorate;
234        (res, r_quorate)
235    };
236    if res == ffi::CS_OK {
237        match r_quorate {
238            0 => Ok(false),
239            1 => Ok(true),
240            _ => Err(CsError::CsErrLibrary),
241        }
242    } else {
243        Err(CsError::from_c(res))
244    }
245}
246
247/// Track node and quorum changes
248pub fn trackstart(handle: Handle, flags: TrackFlags) -> Result<()> {
249    let res = unsafe { ffi::quorum_trackstart(handle.quorum_handle, flags as u32) };
250    if res == ffi::CS_OK {
251        Ok(())
252    } else {
253        Err(CsError::from_c(res))
254    }
255}
256
257/// Stop tracking node and quorum changes
258pub fn trackstop(handle: Handle) -> Result<()> {
259    let res = unsafe { ffi::quorum_trackstop(handle.quorum_handle) };
260    if res == ffi::CS_OK {
261        Ok(())
262    } else {
263        Err(CsError::from_c(res))
264    }
265}
266
267/// Get the current 'context' value for this handle.
268/// The context value is an arbitrary value that is always passed
269/// back to callbacks to help identify the source
270pub fn context_get(handle: Handle) -> Result<u64> {
271    let (res, context) = unsafe {
272        let mut context: u64 = 0;
273        let c_context: *mut c_void = &mut context as *mut _ as *mut c_void;
274        let r = ffi::quorum_context_get(handle.quorum_handle, c_context as *mut *const c_void);
275        (r, context)
276    };
277    if res == ffi::CS_OK {
278        Ok(context)
279    } else {
280        Err(CsError::from_c(res))
281    }
282}
283
284/// Set the current 'context' value for this handle.
285/// The context value is an arbitrary value that is always passed
286/// back to callbacks to help identify the source.
287/// Normally this is set in [initialize], but this allows it to be changed
288pub fn context_set(handle: Handle, context: u64) -> Result<()> {
289    let res = unsafe {
290        let c_context = context as *mut c_void;
291        ffi::quorum_context_set(handle.quorum_handle, c_context)
292    };
293    if res == ffi::CS_OK {
294        Ok(())
295    } else {
296        Err(CsError::from_c(res))
297    }
298}