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
191pub struct Handle {
193 votequorum_handle: u64,
194 callbacks: Callbacks,
195 clone: bool,
196}
197
198impl Clone for Handle {
199 fn clone(&self) -> Handle {
200 Handle {
201 votequorum_handle: self.votequorum_handle,
202 callbacks: self.callbacks,
203 clone: true,
204 }
205 }
206}
207
208impl Drop for Handle {
209 fn drop(self: &mut Handle) {
210 if !self.clone {
211 let _e = finalize(self);
212 }
213 }
214}
215
216impl PartialEq for Handle {
218 fn eq(&self, other: &Handle) -> bool {
219 self.votequorum_handle == other.votequorum_handle
220 }
221}
222
223pub fn initialize(callbacks: &Callbacks) -> Result<Handle> {
227 let mut handle: ffi::votequorum_handle_t = 0;
228
229 let mut c_callbacks = ffi::votequorum_callbacks_t {
230 votequorum_quorum_notify_fn: Some(rust_quorum_notification_fn),
231 votequorum_nodelist_notify_fn: Some(rust_nodelist_notification_fn),
232 votequorum_expectedvotes_notify_fn: Some(rust_expectedvotes_notification_fn),
233 };
234
235 unsafe {
236 let res = ffi::votequorum_initialize(&mut handle, &mut c_callbacks);
237 if res == ffi::CS_OK {
238 let rhandle = Handle {
239 votequorum_handle: handle,
240 callbacks: *callbacks,
241 clone: false,
242 };
243 HANDLE_HASH.lock().unwrap().insert(handle, rhandle.clone());
244 Ok(rhandle)
245 } else {
246 Err(CsError::from_c(res))
247 }
248 }
249}
250
251pub fn finalize(handle: &Handle) -> Result<()> {
253 let res = unsafe { ffi::votequorum_finalize(handle.votequorum_handle) };
254 if res == ffi::CS_OK {
255 HANDLE_HASH
256 .lock()
257 .unwrap()
258 .remove(&handle.votequorum_handle);
259 Ok(())
260 } else {
261 Err(CsError::from_c(res))
262 }
263}
264
265pub fn fd_get(handle: &Handle) -> Result<i32> {
268 let c_fd: *mut c_int = &mut 0 as *mut _ as *mut c_int;
269 let res = unsafe { ffi::votequorum_fd_get(handle.votequorum_handle, c_fd) };
270 if res == ffi::CS_OK {
271 Ok(unsafe { *c_fd })
272 } else {
273 Err(CsError::from_c(res))
274 }
275}
276
277const VOTEQUORUM_QDEVICE_MAX_NAME_LEN: usize = 255;
278
279pub fn get_info(handle: &Handle, nodeid: NodeId) -> Result<NodeInfo> {
281 let mut c_info = ffi::votequorum_info {
282 node_id: 0,
283 node_state: 0,
284 node_votes: 0,
285 node_expected_votes: 0,
286 highest_expected: 0,
287 total_votes: 0,
288 quorum: 0,
289 flags: 0,
290 qdevice_votes: 0,
291 qdevice_name: [0; 255usize],
292 };
293 let res = unsafe {
294 ffi::votequorum_getinfo(handle.votequorum_handle, u32::from(nodeid), &mut c_info)
295 };
296
297 if res == ffi::CS_OK {
298 let info = NodeInfo {
299 node_id: NodeId::from(c_info.node_id),
300 node_state: NodeState::new(c_info.node_state),
301 node_votes: c_info.node_votes,
302 node_expected_votes: c_info.node_expected_votes,
303 highest_expected: c_info.highest_expected,
304 quorum: c_info.quorum,
305 flags: NodeInfoFlags::from_bits(c_info.flags).unwrap_or(NodeInfoFlags::empty()),
306 qdevice_votes: c_info.qdevice_votes,
307 qdevice_name: string_from_bytes(
308 c_info.qdevice_name.as_ptr(),
309 VOTEQUORUM_QDEVICE_MAX_NAME_LEN,
310 )
311 .unwrap_or_default(),
312 };
313 Ok(info)
314 } else {
315 Err(CsError::from_c(res))
316 }
317}
318
319pub fn dispatch(handle: &Handle, flags: DispatchFlags) -> Result<()> {
321 let res = unsafe { ffi::votequorum_dispatch(handle.votequorum_handle, flags as u32) };
322 if res == ffi::CS_OK {
323 Ok(())
324 } else {
325 Err(CsError::from_c(res))
326 }
327}
328
329pub fn trackstart(handle: &Handle, context: u64, flags: TrackFlags) -> Result<()> {
331 let res =
332 unsafe { ffi::votequorum_trackstart(handle.votequorum_handle, context, flags as u32) };
333 if res == ffi::CS_OK {
334 Ok(())
335 } else {
336 Err(CsError::from_c(res))
337 }
338}
339
340pub fn trackstop(handle: &Handle) -> Result<()> {
342 let res = unsafe { ffi::votequorum_trackstop(handle.votequorum_handle) };
343 if res == ffi::CS_OK {
344 Ok(())
345 } else {
346 Err(CsError::from_c(res))
347 }
348}
349
350pub fn context_get(handle: &Handle) -> Result<u64> {
354 let (res, context) = unsafe {
355 let mut c_context: *mut c_void = &mut 0u64 as *mut _ as *mut c_void;
356 let r = ffi::votequorum_context_get(handle.votequorum_handle, &mut c_context);
357 let context: u64 = c_context as u64;
358 (r, context)
359 };
360 if res == ffi::CS_OK {
361 Ok(context)
362 } else {
363 Err(CsError::from_c(res))
364 }
365}
366
367pub fn context_set(handle: &Handle, context: u64) -> Result<()> {
372 let res = unsafe {
373 let c_context = context as *mut c_void;
374 ffi::votequorum_context_set(handle.votequorum_handle, c_context)
375 };
376 if res == ffi::CS_OK {
377 Ok(())
378 } else {
379 Err(CsError::from_c(res))
380 }
381}
382
383pub fn set_expected(handle: &Handle, expected_votes: u32) -> Result<()> {
386 let res = unsafe { ffi::votequorum_setexpected(handle.votequorum_handle, expected_votes) };
387 if res == ffi::CS_OK {
388 Ok(())
389 } else {
390 Err(CsError::from_c(res))
391 }
392}
393
394pub fn set_votes(handle: &Handle, nodeid: NodeId, votes: u32) -> Result<()> {
396 let res =
397 unsafe { ffi::votequorum_setvotes(handle.votequorum_handle, u32::from(nodeid), votes) };
398 if res == ffi::CS_OK {
399 Ok(())
400 } else {
401 Err(CsError::from_c(res))
402 }
403}
404
405pub fn qdevice_register(handle: &Handle, name: &str) -> Result<()> {
407 let c_string = {
408 match CString::new(name) {
409 Ok(cs) => cs,
410 Err(_) => return Err(CsError::CsErrInvalidParam),
411 }
412 };
413
414 let res =
415 unsafe { ffi::votequorum_qdevice_register(handle.votequorum_handle, c_string.as_ptr()) };
416 if res == ffi::CS_OK {
417 Ok(())
418 } else {
419 Err(CsError::from_c(res))
420 }
421}
422
423pub fn qdevice_unregister(handle: &Handle, name: &str) -> Result<()> {
425 let c_string = {
426 match CString::new(name) {
427 Ok(cs) => cs,
428 Err(_) => return Err(CsError::CsErrInvalidParam),
429 }
430 };
431
432 let res =
433 unsafe { ffi::votequorum_qdevice_unregister(handle.votequorum_handle, c_string.as_ptr()) };
434 if res == ffi::CS_OK {
435 Ok(())
436 } else {
437 Err(CsError::from_c(res))
438 }
439}
440
441pub fn qdevice_update(handle: &Handle, oldname: &str, newname: &str) -> Result<()> {
443 let on_string = {
444 match CString::new(oldname) {
445 Ok(cs) => cs,
446 Err(_) => return Err(CsError::CsErrInvalidParam),
447 }
448 };
449 let nn_string = {
450 match CString::new(newname) {
451 Ok(cs) => cs,
452 Err(_) => return Err(CsError::CsErrInvalidParam),
453 }
454 };
455
456 let res = unsafe {
457 ffi::votequorum_qdevice_update(
458 handle.votequorum_handle,
459 on_string.as_ptr(),
460 nn_string.as_ptr(),
461 )
462 };
463 if res == ffi::CS_OK {
464 Ok(())
465 } else {
466 Err(CsError::from_c(res))
467 }
468}
469
470pub fn qdevice_poll(handle: &Handle, name: &str, cast_vote: bool, ring_id: &RingId) -> Result<()> {
474 let c_string = {
475 match CString::new(name) {
476 Ok(cs) => cs,
477 Err(_) => return Err(CsError::CsErrInvalidParam),
478 }
479 };
480
481 let c_cast_vote: u32 = u32::from(cast_vote);
482 let c_ring_id = ffi::votequorum_ring_id_t {
483 nodeid: u32::from(ring_id.nodeid),
484 seq: ring_id.seq,
485 };
486
487 let res = unsafe {
488 ffi::votequorum_qdevice_poll(
489 handle.votequorum_handle,
490 c_string.as_ptr(),
491 c_cast_vote,
492 c_ring_id,
493 )
494 };
495 if res == ffi::CS_OK {
496 Ok(())
497 } else {
498 Err(CsError::from_c(res))
499 }
500}
501
502pub fn qdevice_master_wins(handle: &Handle, name: &str, master_wins: bool) -> Result<()> {
504 let c_string = {
505 match CString::new(name) {
506 Ok(cs) => cs,
507 Err(_) => return Err(CsError::CsErrInvalidParam),
508 }
509 };
510
511 let c_master_wins: u32 = u32::from(master_wins);
512
513 let res = unsafe {
514 ffi::votequorum_qdevice_master_wins(
515 handle.votequorum_handle,
516 c_string.as_ptr(),
517 c_master_wins,
518 )
519 };
520 if res == ffi::CS_OK {
521 Ok(())
522 } else {
523 Err(CsError::from_c(res))
524 }
525}