1#![allow(clippy::type_complexity)]
10#![allow(clippy::needless_range_loop)]
11#![allow(clippy::single_match)]
12
13use crate::sys::votequorum as ffi;
15
16use std::collections::HashMap;
17use std::ffi::CString;
18use std::fmt;
19use std::os::raw::{c_int, c_void};
20use std::slice;
21use std::sync::Mutex;
22
23use crate::string_from_bytes;
24use crate::{CsError, DispatchFlags, NodeId, Result, TrackFlags};
25
26pub struct RingId {
28 pub nodeid: NodeId,
29 pub seq: u64,
30}
31
32lazy_static! {
34 static ref HANDLE_HASH: Mutex<HashMap<u64, Handle>> = Mutex::new(HashMap::new());
35}
36
37pub enum NodeState {
39 Member,
40 Dead,
41 Leaving,
42 Unknown,
43}
44impl NodeState {
45 pub fn new(state: u32) -> NodeState {
46 match state {
47 1 => NodeState::Member,
48 2 => NodeState::Dead,
49 3 => NodeState::Leaving,
50 _ => NodeState::Unknown,
51 }
52 }
53}
54impl fmt::Debug for NodeState {
55 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
56 match self {
57 NodeState::Member => write!(f, "Member"),
58 NodeState::Dead => write!(f, "Dead"),
59 NodeState::Leaving => write!(f, "Leaving"),
60 _ => write!(f, "Unknown"),
61 }
62 }
63}
64
65pub struct Node {
67 nodeid: NodeId,
68 state: NodeState,
69}
70impl fmt::Debug for Node {
71 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
72 write!(f, "nodeid: {}, state: {:?}", self.nodeid, self.state)
73 }
74}
75
76bitflags! {
77pub struct NodeInfoFlags: u32
79 {
80 const VOTEQUORUM_INFO_TWONODE = 1;
81 const VOTEQUORUM_INFO_QUORATE = 2;
82 const VOTEQUORUM_INFO_WAIT_FOR_ALL = 4;
83 const VOTEQUORUM_INFO_LAST_MAN_STANDING = 8;
84 const VOTEQUORUM_INFO_AUTO_TIE_BREAKER = 16;
85 const VOTEQUORUM_INFO_ALLOW_DOWNSCALE = 32;
86 const VOTEQUORUM_INFO_QDEVICE_REGISTERED = 64;
87 const VOTEQUORUM_INFO_QDEVICE_ALIVE = 128;
88 const VOTEQUORUM_INFO_QDEVICE_CAST_VOTE = 256;
89 const VOTEQUORUM_INFO_QDEVICE_MASTER_WINS = 512;
90 }
91}
92
93pub struct NodeInfo {
95 pub node_id: NodeId,
96 pub node_state: NodeState,
97 pub node_votes: u32,
98 pub node_expected_votes: u32,
99 pub highest_expected: u32,
100 pub quorum: u32,
101 pub flags: NodeInfoFlags,
102 pub qdevice_votes: u32,
103 pub qdevice_name: String,
104}
105
106fn list_to_vec(list_entries: u32, list: *const u32) -> Vec<NodeId> {
108 let mut r_member_list = Vec::<NodeId>::new();
109 let temp_members: &[u32] = unsafe { slice::from_raw_parts(list, list_entries as usize) };
110 for i in 0..list_entries as usize {
111 r_member_list.push(NodeId::from(temp_members[i]));
112 }
113 r_member_list
114}
115
116extern "C" fn rust_expectedvotes_notification_fn(
118 handle: ffi::votequorum_handle_t,
119 context: u64,
120 expected_votes: u32,
121) {
122 if let Some(h) = HANDLE_HASH.lock().unwrap().get(&handle) {
123 if let Some(cb) = h.callbacks.expectedvotes_notification_fn {
124 (cb)(h, context, expected_votes);
125 }
126 }
127}
128
129extern "C" fn rust_quorum_notification_fn(
131 handle: ffi::votequorum_handle_t,
132 context: u64,
133 quorate: u32,
134 node_list_entries: u32,
135 node_list: *mut ffi::votequorum_node_t,
136) {
137 if let Some(h) = HANDLE_HASH.lock().unwrap().get(&handle) {
138 let r_quorate = match quorate {
139 0 => false,
140 1 => true,
141 _ => false,
142 };
143 let mut r_node_list = Vec::<Node>::new();
144 let temp_members: &[ffi::votequorum_node_t] =
145 unsafe { slice::from_raw_parts(node_list, node_list_entries as usize) };
146 for i in 0..node_list_entries as usize {
147 r_node_list.push(Node {
148 nodeid: NodeId::from(temp_members[i].nodeid),
149 state: NodeState::new(temp_members[i].state),
150 });
151 }
152 if let Some(cb) = h.callbacks.quorum_notification_fn {
153 (cb)(h, context, r_quorate, r_node_list);
154 }
155 }
156}
157
158extern "C" fn rust_nodelist_notification_fn(
160 handle: ffi::votequorum_handle_t,
161 context: u64,
162 ring_id: ffi::votequorum_ring_id_t,
163 node_list_entries: u32,
164 node_list: *mut u32,
165) {
166 if let Some(h) = HANDLE_HASH.lock().unwrap().get(&handle) {
167 let r_ring_id = RingId {
168 nodeid: NodeId::from(ring_id.nodeid),
169 seq: ring_id.seq,
170 };
171
172 let r_node_list = list_to_vec(node_list_entries, node_list);
173
174 if let Some(cb) = h.callbacks.nodelist_notification_fn {
175 (cb)(h, context, r_ring_id, r_node_list);
176 }
177 }
178}
179
180#[derive(Copy, Clone)]
182pub struct Callbacks {
183 pub quorum_notification_fn:
184 Option<fn(hande: &Handle, context: u64, quorate: bool, node_list: Vec<Node>)>,
185 pub nodelist_notification_fn:
186 Option<fn(hande: &Handle, context: u64, ring_id: RingId, node_list: Vec<NodeId>)>,
187 pub expectedvotes_notification_fn:
188 Option<fn(handle: &Handle, context: u64, expected_votes: u32)>,
189}
190
191#[derive(Copy, Clone)]
193pub struct Handle {
194 votequorum_handle: u64,
195 callbacks: Callbacks,
196}
197
198pub fn initialize(callbacks: &Callbacks) -> Result<Handle> {
202 let mut handle: ffi::votequorum_handle_t = 0;
203
204 let mut c_callbacks = ffi::votequorum_callbacks_t {
205 votequorum_quorum_notify_fn: Some(rust_quorum_notification_fn),
206 votequorum_nodelist_notify_fn: Some(rust_nodelist_notification_fn),
207 votequorum_expectedvotes_notify_fn: Some(rust_expectedvotes_notification_fn),
208 };
209
210 unsafe {
211 let res = ffi::votequorum_initialize(&mut handle, &mut c_callbacks);
212 if res == ffi::CS_OK {
213 let rhandle = Handle {
214 votequorum_handle: handle,
215 callbacks: *callbacks,
216 };
217 HANDLE_HASH.lock().unwrap().insert(handle, rhandle);
218 Ok(rhandle)
219 } else {
220 Err(CsError::from_c(res))
221 }
222 }
223}
224
225pub fn finalize(handle: Handle) -> Result<()> {
227 let res = unsafe { ffi::votequorum_finalize(handle.votequorum_handle) };
228 if res == ffi::CS_OK {
229 HANDLE_HASH
230 .lock()
231 .unwrap()
232 .remove(&handle.votequorum_handle);
233 Ok(())
234 } else {
235 Err(CsError::from_c(res))
236 }
237}
238
239pub fn fd_get(handle: Handle) -> Result<i32> {
242 let c_fd: *mut c_int = &mut 0 as *mut _ as *mut c_int;
243 let res = unsafe { ffi::votequorum_fd_get(handle.votequorum_handle, c_fd) };
244 if res == ffi::CS_OK {
245 Ok(c_fd as i32)
246 } else {
247 Err(CsError::from_c(res))
248 }
249}
250
251const VOTEQUORUM_QDEVICE_MAX_NAME_LEN: usize = 255;
252
253pub fn get_info(handle: Handle, nodeid: NodeId) -> Result<NodeInfo> {
255 let mut c_info = ffi::votequorum_info {
256 node_id: 0,
257 node_state: 0,
258 node_votes: 0,
259 node_expected_votes: 0,
260 highest_expected: 0,
261 total_votes: 0,
262 quorum: 0,
263 flags: 0,
264 qdevice_votes: 0,
265 qdevice_name: [0; 255usize],
266 };
267 let res = unsafe {
268 ffi::votequorum_getinfo(handle.votequorum_handle, u32::from(nodeid), &mut c_info)
269 };
270
271 if res == ffi::CS_OK {
272 let info = NodeInfo {
273 node_id: NodeId::from(c_info.node_id),
274 node_state: NodeState::new(c_info.node_state),
275 node_votes: c_info.node_votes,
276 node_expected_votes: c_info.node_expected_votes,
277 highest_expected: c_info.highest_expected,
278 quorum: c_info.quorum,
279 flags: NodeInfoFlags { bits: c_info.flags },
280 qdevice_votes: c_info.qdevice_votes,
281 qdevice_name: match string_from_bytes(
282 c_info.qdevice_name.as_ptr(),
283 VOTEQUORUM_QDEVICE_MAX_NAME_LEN,
284 ) {
285 Ok(s) => s,
286 Err(_) => String::new(),
287 },
288 };
289 Ok(info)
290 } else {
291 Err(CsError::from_c(res))
292 }
293}
294
295pub fn dispatch(handle: Handle, flags: DispatchFlags) -> Result<()> {
297 let res = unsafe { ffi::votequorum_dispatch(handle.votequorum_handle, flags as u32) };
298 if res == ffi::CS_OK {
299 Ok(())
300 } else {
301 Err(CsError::from_c(res))
302 }
303}
304
305pub fn trackstart(handle: Handle, context: u64, flags: TrackFlags) -> Result<()> {
307 let res =
308 unsafe { ffi::votequorum_trackstart(handle.votequorum_handle, context, flags as u32) };
309 if res == ffi::CS_OK {
310 Ok(())
311 } else {
312 Err(CsError::from_c(res))
313 }
314}
315
316pub fn trackstop(handle: Handle) -> Result<()> {
318 let res = unsafe { ffi::votequorum_trackstop(handle.votequorum_handle) };
319 if res == ffi::CS_OK {
320 Ok(())
321 } else {
322 Err(CsError::from_c(res))
323 }
324}
325
326pub fn context_get(handle: Handle) -> Result<u64> {
330 let (res, context) = unsafe {
331 let mut c_context: *mut c_void = &mut 0u64 as *mut _ as *mut c_void;
332 let r = ffi::votequorum_context_get(handle.votequorum_handle, &mut c_context);
333 let context: u64 = c_context as u64;
334 (r, context)
335 };
336 if res == ffi::CS_OK {
337 Ok(context)
338 } else {
339 Err(CsError::from_c(res))
340 }
341}
342
343pub fn context_set(handle: Handle, context: u64) -> Result<()> {
348 let res = unsafe {
349 let c_context = context as *mut c_void;
350 ffi::votequorum_context_set(handle.votequorum_handle, c_context)
351 };
352 if res == ffi::CS_OK {
353 Ok(())
354 } else {
355 Err(CsError::from_c(res))
356 }
357}
358
359pub fn set_expected(handle: Handle, expected_votes: u32) -> Result<()> {
362 let res = unsafe { ffi::votequorum_setexpected(handle.votequorum_handle, expected_votes) };
363 if res == ffi::CS_OK {
364 Ok(())
365 } else {
366 Err(CsError::from_c(res))
367 }
368}
369
370pub fn set_votes(handle: Handle, nodeid: NodeId, votes: u32) -> Result<()> {
372 let res =
373 unsafe { ffi::votequorum_setvotes(handle.votequorum_handle, u32::from(nodeid), votes) };
374 if res == ffi::CS_OK {
375 Ok(())
376 } else {
377 Err(CsError::from_c(res))
378 }
379}
380
381pub fn qdevice_register(handle: Handle, name: &str) -> Result<()> {
383 let c_string = {
384 match CString::new(name) {
385 Ok(cs) => cs,
386 Err(_) => return Err(CsError::CsErrInvalidParam),
387 }
388 };
389
390 let res =
391 unsafe { ffi::votequorum_qdevice_register(handle.votequorum_handle, c_string.as_ptr()) };
392 if res == ffi::CS_OK {
393 Ok(())
394 } else {
395 Err(CsError::from_c(res))
396 }
397}
398
399pub fn qdevice_unregister(handle: Handle, name: &str) -> Result<()> {
401 let c_string = {
402 match CString::new(name) {
403 Ok(cs) => cs,
404 Err(_) => return Err(CsError::CsErrInvalidParam),
405 }
406 };
407
408 let res =
409 unsafe { ffi::votequorum_qdevice_unregister(handle.votequorum_handle, c_string.as_ptr()) };
410 if res == ffi::CS_OK {
411 Ok(())
412 } else {
413 Err(CsError::from_c(res))
414 }
415}
416
417pub fn qdevice_update(handle: Handle, oldname: &str, newname: &str) -> Result<()> {
419 let on_string = {
420 match CString::new(oldname) {
421 Ok(cs) => cs,
422 Err(_) => return Err(CsError::CsErrInvalidParam),
423 }
424 };
425 let nn_string = {
426 match CString::new(newname) {
427 Ok(cs) => cs,
428 Err(_) => return Err(CsError::CsErrInvalidParam),
429 }
430 };
431
432 let res = unsafe {
433 ffi::votequorum_qdevice_update(
434 handle.votequorum_handle,
435 on_string.as_ptr(),
436 nn_string.as_ptr(),
437 )
438 };
439 if res == ffi::CS_OK {
440 Ok(())
441 } else {
442 Err(CsError::from_c(res))
443 }
444}
445
446pub fn qdevice_poll(handle: Handle, name: &str, cast_vote: bool, ring_id: &RingId) -> Result<()> {
450 let c_string = {
451 match CString::new(name) {
452 Ok(cs) => cs,
453 Err(_) => return Err(CsError::CsErrInvalidParam),
454 }
455 };
456
457 let c_cast_vote: u32 = u32::from(cast_vote);
458 let c_ring_id = ffi::votequorum_ring_id_t {
459 nodeid: u32::from(ring_id.nodeid),
460 seq: ring_id.seq,
461 };
462
463 let res = unsafe {
464 ffi::votequorum_qdevice_poll(
465 handle.votequorum_handle,
466 c_string.as_ptr(),
467 c_cast_vote,
468 c_ring_id,
469 )
470 };
471 if res == ffi::CS_OK {
472 Ok(())
473 } else {
474 Err(CsError::from_c(res))
475 }
476}
477
478pub fn qdevice_master_wins(handle: Handle, name: &str, master_wins: bool) -> Result<()> {
480 let c_string = {
481 match CString::new(name) {
482 Ok(cs) => cs,
483 Err(_) => return Err(CsError::CsErrInvalidParam),
484 }
485 };
486
487 let c_master_wins: u32 = u32::from(master_wins);
488
489 let res = unsafe {
490 ffi::votequorum_qdevice_master_wins(
491 handle.votequorum_handle,
492 c_string.as_ptr(),
493 c_master_wins,
494 )
495 };
496 if res == ffi::CS_OK {
497 Ok(())
498 } else {
499 Err(CsError::from_c(res))
500 }
501}