quorum.rs 9.4 KB

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