From dffe2ea59e47b31ec23001194d987a496a83669b Mon Sep 17 00:00:00 2001 From: bizhiyuan Date: Thu, 19 Sep 2024 19:15:51 +0800 Subject: [PATCH] rust: remove rust bindings rust: tests return errors and don't hang (cherry picked from commit c7910a872f87e0e1a315d50dc181741fce5d39c3) --- backport-rust-Improve-Rust-bindings.patch | 1310 +++++++++++++++++ ...t-tests-return-errors-and-don-t-hang.patch | 494 +++++++ corosync.spec | 9 +- 3 files changed, 1811 insertions(+), 2 deletions(-) create mode 100644 backport-rust-Improve-Rust-bindings.patch create mode 100644 backport-rust-tests-return-errors-and-don-t-hang.patch diff --git a/backport-rust-Improve-Rust-bindings.patch b/backport-rust-Improve-Rust-bindings.patch new file mode 100644 index 0000000..f4575ce --- /dev/null +++ b/backport-rust-Improve-Rust-bindings.patch @@ -0,0 +1,1310 @@ +From 58d654261a3bbd04cb19b1ddc85d8d872f94384e Mon Sep 17 00:00:00 2001 +From: Christine Caulfield +Date: Mon, 8 Jan 2024 14:29:36 +0000 +Subject: [PATCH] rust: Improve Rust bindings + +The big change here is that all API calls now take a &Handle +rather than making a copy. This, apart from being more sensible +and efficient, allows us to implment Drop on the handle so that +it will call _free() when it goes out of scope. + +There's some jiggery-pokery with a clone flag in there now +because of callbacks that can return a valid handle, and we +want those to be Drop'ed sensibly. + +Signed-off-by: Christine Caulfield +Reviewed-by: Jan Friesse +--- + bindings/rust/src/cfg.rs | 54 +++++++++---- + bindings/rust/src/cmap.rs | 76 ++++++++++++------- + bindings/rust/src/cpg.rs | 56 ++++++++++---- + bindings/rust/src/quorum.rs | 46 ++++++++--- + bindings/rust/src/votequorum.rs | 60 ++++++++++----- + bindings/rust/tests/src/bin/cfg-test.rs | 20 ++--- + bindings/rust/tests/src/bin/cmap-test.rs | 34 ++++----- + bindings/rust/tests/src/bin/cpg-test.rs | 16 ++-- + bindings/rust/tests/src/bin/quorum-test.rs | 8 +- + .../rust/tests/src/bin/votequorum-test.rs | 14 ++-- + 10 files changed, 256 insertions(+), 128 deletions(-) + +diff --git a/bindings/rust/src/cfg.rs b/bindings/rust/src/cfg.rs +index b4eecacc..5e1a0442 100644 +--- a/bindings/rust/src/cfg.rs ++++ b/bindings/rust/src/cfg.rs +@@ -31,10 +31,35 @@ pub struct Callbacks { + } + + /// A handle into the cfg library. returned from [initialize] and needed for all other calls +-#[derive(Copy, Clone)] + pub struct Handle { + cfg_handle: u64, + callbacks: Callbacks, ++ clone: bool, ++} ++ ++impl Clone for Handle { ++ fn clone(&self) -> Handle { ++ Handle { ++ cfg_handle: self.cfg_handle, ++ callbacks: self.callbacks, ++ clone: true, ++ } ++ } ++} ++ ++impl Drop for Handle { ++ fn drop(self: &mut Handle) { ++ if !self.clone { ++ let _e = finalize(self); ++ } ++ } ++} ++ ++// Clones count as equivalent ++impl PartialEq for Handle { ++ fn eq(&self, other: &Handle) -> bool { ++ self.cfg_handle == other.cfg_handle ++ } + } + + /// Flags for [try_shutdown] +@@ -114,8 +139,9 @@ pub fn initialize(callbacks: &Callbacks) -> Result { + let rhandle = Handle { + cfg_handle: handle, + callbacks: *callbacks, ++ clone: false, + }; +- HANDLE_HASH.lock().unwrap().insert(handle, rhandle); ++ HANDLE_HASH.lock().unwrap().insert(handle, rhandle.clone()); + Ok(rhandle) + } else { + Err(CsError::from_c(res)) +@@ -124,7 +150,7 @@ pub fn initialize(callbacks: &Callbacks) -> Result { + } + + /// Finish with a connection to corosync, after calling this the [Handle] is invalid +-pub fn finalize(handle: Handle) -> Result<()> { ++pub fn finalize(handle: &Handle) -> Result<()> { + let res = unsafe { ffi::corosync_cfg_finalize(handle.cfg_handle) }; + if res == ffi::CS_OK { + HANDLE_HASH.lock().unwrap().remove(&handle.cfg_handle); +@@ -136,7 +162,7 @@ pub fn finalize(handle: Handle) -> Result<()> { + + // not sure if an fd is the right thing to return here, but it will do for now. + /// Returns a file descriptor to use for poll/select on the CFG handle +-pub fn fd_get(handle: Handle) -> Result { ++pub fn fd_get(handle: &Handle) -> Result { + let c_fd: *mut c_int = &mut 0 as *mut _ as *mut c_int; + let res = unsafe { ffi::corosync_cfg_fd_get(handle.cfg_handle, c_fd) }; + if res == ffi::CS_OK { +@@ -147,7 +173,7 @@ pub fn fd_get(handle: Handle) -> Result { + } + + /// Get the local [NodeId] +-pub fn local_get(handle: Handle) -> Result { ++pub fn local_get(handle: &Handle) -> Result { + let mut nodeid: u32 = 0; + let res = unsafe { ffi::corosync_cfg_local_get(handle.cfg_handle, &mut nodeid) }; + if res == ffi::CS_OK { +@@ -158,7 +184,7 @@ pub fn local_get(handle: Handle) -> Result { + } + + /// Reload the cluster configuration on all nodes +-pub fn reload_cnfig(handle: Handle) -> Result<()> { ++pub fn reload_cnfig(handle: &Handle) -> Result<()> { + let res = unsafe { ffi::corosync_cfg_reload_config(handle.cfg_handle) }; + if res == ffi::CS_OK { + Ok(()) +@@ -168,7 +194,7 @@ pub fn reload_cnfig(handle: Handle) -> Result<()> { + } + + /// Re-open the cluster log files, on this node only +-pub fn reopen_log_files(handle: Handle) -> Result<()> { ++pub fn reopen_log_files(handle: &Handle) -> Result<()> { + let res = unsafe { ffi::corosync_cfg_reopen_log_files(handle.cfg_handle) }; + if res == ffi::CS_OK { + Ok(()) +@@ -179,7 +205,7 @@ pub fn reopen_log_files(handle: Handle) -> Result<()> { + + /// Tell another cluster node to shutdown. reason is a string that + /// will be written to the system log files. +-pub fn kill_node(handle: Handle, nodeid: NodeId, reason: &str) -> Result<()> { ++pub fn kill_node(handle: &Handle, nodeid: NodeId, reason: &str) -> Result<()> { + let c_string = { + match CString::new(reason) { + Ok(cs) => cs, +@@ -200,7 +226,7 @@ pub fn kill_node(handle: Handle, nodeid: NodeId, reason: &str) -> Result<()> { + /// Ask this cluster node to shutdown. If [ShutdownFlags] is set to Request then + ///it may be refused by other applications + /// that have registered for shutdown callbacks. +-pub fn try_shutdown(handle: Handle, flags: ShutdownFlags) -> Result<()> { ++pub fn try_shutdown(handle: &Handle, flags: ShutdownFlags) -> Result<()> { + let c_flags = match flags { + ShutdownFlags::Request => 0, + ShutdownFlags::Regardless => 1, +@@ -215,7 +241,7 @@ pub fn try_shutdown(handle: Handle, flags: ShutdownFlags) -> Result<()> { + } + + /// Reply to a shutdown request with Yes or No [ShutdownReply] +-pub fn reply_to_shutdown(handle: Handle, flags: ShutdownReply) -> Result<()> { ++pub fn reply_to_shutdown(handle: &Handle, flags: ShutdownReply) -> Result<()> { + let c_flags = match flags { + ShutdownReply::No => 0, + ShutdownReply::Yes => 1, +@@ -229,7 +255,7 @@ pub fn reply_to_shutdown(handle: Handle, flags: ShutdownReply) -> Result<()> { + } + + /// Call any/all active CFG callbacks for this [Handle] see [DispatchFlags] for details +-pub fn dispatch(handle: Handle, flags: DispatchFlags) -> Result<()> { ++pub fn dispatch(handle: &Handle, flags: DispatchFlags) -> Result<()> { + let res = unsafe { ffi::corosync_cfg_dispatch(handle.cfg_handle, flags as u32) }; + if res == ffi::CS_OK { + Ok(()) +@@ -293,7 +319,7 @@ fn new_ls() -> ffi::corosync_knet_link_status_v1 { + /// Get the extended status of a node in the cluster (including active links) from its [NodeId]. + /// Returns a filled in [NodeStatus] struct + pub fn node_status_get( +- handle: Handle, ++ handle: &Handle, + nodeid: NodeId, + _version: NodeStatusVersion, + ) -> Result { +@@ -328,7 +354,7 @@ pub fn node_status_get( + } + + /// Start tracking for shutdown notifications +-pub fn track_start(handle: Handle, _flags: TrackFlags) -> Result<()> { ++pub fn track_start(handle: &Handle, _flags: TrackFlags) -> Result<()> { + let res = unsafe { ffi::corosync_cfg_trackstart(handle.cfg_handle, 0) }; + if res == ffi::CS_OK { + Ok(()) +@@ -338,7 +364,7 @@ pub fn track_start(handle: Handle, _flags: TrackFlags) -> Result<()> { + } + + /// Stop tracking for shutdown notifications +-pub fn track_stop(handle: Handle) -> Result<()> { ++pub fn track_stop(handle: &Handle) -> Result<()> { + let res = unsafe { ffi::corosync_cfg_trackstop(handle.cfg_handle) }; + if res == ffi::CS_OK { + Ok(()) +diff --git a/bindings/rust/src/cmap.rs b/bindings/rust/src/cmap.rs +index 454fbee2..4f3651aa 100644 +--- a/bindings/rust/src/cmap.rs ++++ b/bindings/rust/src/cmap.rs +@@ -62,10 +62,33 @@ impl fmt::Display for TrackType { + } + } + +-#[derive(Copy, Clone)] + /// A handle returned from [initialize], needs to be passed to all other cmap API calls + pub struct Handle { + cmap_handle: u64, ++ clone: bool, ++} ++ ++impl Clone for Handle { ++ fn clone(&self) -> Handle { ++ Handle { ++ cmap_handle: self.cmap_handle, ++ clone: true, ++ } ++ } ++} ++ ++impl Drop for Handle { ++ fn drop(self: &mut Handle) { ++ if !self.clone { ++ let _e = finalize(self); ++ } ++ } ++} ++// Clones count as equivalent ++impl PartialEq for Handle { ++ fn eq(&self, other: &Handle) -> bool { ++ self.cmap_handle == other.cmap_handle ++ } + } + + #[derive(Copy, Clone)] +@@ -97,8 +120,9 @@ pub fn initialize(map: Map) -> Result { + if res == ffi::CS_OK { + let rhandle = Handle { + cmap_handle: handle, ++ clone: false, + }; +- HANDLE_HASH.lock().unwrap().insert(handle, rhandle); ++ HANDLE_HASH.lock().unwrap().insert(handle, rhandle.clone()); + Ok(rhandle) + } else { + Err(CsError::from_c(res)) +@@ -108,7 +132,7 @@ pub fn initialize(map: Map) -> Result { + + /// Finish with a connection to corosync. + /// Takes a [Handle] as returned from [initialize] +-pub fn finalize(handle: Handle) -> Result<()> { ++pub fn finalize(handle: &Handle) -> Result<()> { + let res = unsafe { ffi::cmap_finalize(handle.cmap_handle) }; + if res == ffi::CS_OK { + HANDLE_HASH.lock().unwrap().remove(&handle.cmap_handle); +@@ -121,7 +145,7 @@ pub fn finalize(handle: Handle) -> Result<()> { + /// Return a file descriptor to use for poll/select on the CMAP handle. + /// Takes a [Handle] as returned from [initialize], + /// returns a C file descriptor as i32 +-pub fn fd_get(handle: Handle) -> Result { ++pub fn fd_get(handle: &Handle) -> Result { + let c_fd: *mut c_int = &mut 0 as *mut _ as *mut c_int; + let res = unsafe { ffi::cmap_fd_get(handle.cmap_handle, c_fd) }; + if res == ffi::CS_OK { +@@ -134,7 +158,7 @@ pub fn fd_get(handle: Handle) -> Result { + /// Dispatch any/all active CMAP callbacks. + /// Takes a [Handle] as returned from [initialize], + /// flags [DispatchFlags] tells it how many items to dispatch before returning +-pub fn dispatch(handle: Handle, flags: DispatchFlags) -> Result<()> { ++pub fn dispatch(handle: &Handle, flags: DispatchFlags) -> Result<()> { + let res = unsafe { ffi::cmap_dispatch(handle.cmap_handle, flags as u32) }; + if res == ffi::CS_OK { + Ok(()) +@@ -146,7 +170,7 @@ pub fn dispatch(handle: Handle, flags: DispatchFlags) -> Result<()> { + /// Get the current 'context' value for this handle + /// The context value is an arbitrary value that is always passed + /// back to callbacks to help identify the source +-pub fn context_get(handle: Handle) -> Result { ++pub fn context_get(handle: &Handle) -> Result { + let (res, context) = unsafe { + let mut context: u64 = 0; + let c_context: *mut c_void = &mut context as *mut _ as *mut c_void; +@@ -164,7 +188,7 @@ pub fn context_get(handle: Handle) -> Result { + /// The context value is an arbitrary value that is always passed + /// back to callbacks to help identify the source. + /// Normally this is set in [initialize], but this allows it to be changed +-pub fn context_set(handle: Handle, context: u64) -> Result<()> { ++pub fn context_set(handle: &Handle, context: u64) -> Result<()> { + let res = unsafe { + let c_context = context as *mut c_void; + ffi::cmap_context_set(handle.cmap_handle, c_context) +@@ -274,7 +298,7 @@ fn string_to_cstring_validated(key: &str, maxlen: usize) -> Result { + } + + fn set_value( +- handle: Handle, ++ handle: &Handle, + key_name: &str, + datatype: DataType, + value: *mut c_void, +@@ -333,7 +357,7 @@ fn is_numeric_type(dtype: DataType) -> bool { + + /// Function to set a generic numeric value + /// This doesn't work for strings or binaries +-pub fn set_number(handle: Handle, key_name: &str, value: T) -> Result<()> { ++pub fn set_number(handle: &Handle, key_name: &str, value: T) -> Result<()> { + let (c_type, c_size) = generic_to_cmap(value); + + if is_numeric_type(c_type) { +@@ -345,21 +369,21 @@ pub fn set_number(handle: Handle, key_name: &str, value: T) -> Result<( + } + } + +-pub fn set_u8(handle: Handle, key_name: &str, value: u8) -> Result<()> { ++pub fn set_u8(handle: &Handle, key_name: &str, value: u8) -> Result<()> { + let mut tmp = value; + let c_value: *mut c_void = &mut tmp as *mut _ as *mut c_void; + set_value(handle, key_name, DataType::UInt8, c_value as *mut c_void, 1) + } + + /// Sets an i8 value into cmap +-pub fn set_i8(handle: Handle, key_name: &str, value: i8) -> Result<()> { ++pub fn set_i8(handle: &Handle, key_name: &str, value: i8) -> Result<()> { + let mut tmp = value; + let c_value: *mut c_void = &mut tmp as *mut _ as *mut c_void; + set_value(handle, key_name, DataType::Int8, c_value as *mut c_void, 1) + } + + /// Sets a u16 value into cmap +-pub fn set_u16(handle: Handle, key_name: &str, value: u16) -> Result<()> { ++pub fn set_u16(handle: &Handle, key_name: &str, value: u16) -> Result<()> { + let mut tmp = value; + let c_value: *mut c_void = &mut tmp as *mut _ as *mut c_void; + set_value( +@@ -372,28 +396,28 @@ pub fn set_u16(handle: Handle, key_name: &str, value: u16) -> Result<()> { + } + + /// Sets an i16 value into cmap +-pub fn set_i16(handle: Handle, key_name: &str, value: i16) -> Result<()> { ++pub fn set_i16(handle: &Handle, key_name: &str, value: i16) -> Result<()> { + let mut tmp = value; + let c_value: *mut c_void = &mut tmp as *mut _ as *mut c_void; + set_value(handle, key_name, DataType::Int16, c_value as *mut c_void, 2) + } + + /// Sets a u32 value into cmap +-pub fn set_u32(handle: Handle, key_name: &str, value: u32) -> Result<()> { ++pub fn set_u32(handle: &Handle, key_name: &str, value: u32) -> Result<()> { + let mut tmp = value; + let c_value: *mut c_void = &mut tmp as *mut _ as *mut c_void; + set_value(handle, key_name, DataType::UInt32, c_value, 4) + } + + /// Sets an i32 value into cmap +-pub fn set_i132(handle: Handle, key_name: &str, value: i32) -> Result<()> { ++pub fn set_i132(handle: &Handle, key_name: &str, value: i32) -> Result<()> { + let mut tmp = value; + let c_value: *mut c_void = &mut tmp as *mut _ as *mut c_void; + set_value(handle, key_name, DataType::Int32, c_value as *mut c_void, 4) + } + + /// Sets a u64 value into cmap +-pub fn set_u64(handle: Handle, key_name: &str, value: u64) -> Result<()> { ++pub fn set_u64(handle: &Handle, key_name: &str, value: u64) -> Result<()> { + let mut tmp = value; + let c_value: *mut c_void = &mut tmp as *mut _ as *mut c_void; + set_value( +@@ -406,14 +430,14 @@ pub fn set_u64(handle: Handle, key_name: &str, value: u64) -> Result<()> { + } + + /// Sets an i64 value into cmap +-pub fn set_i164(handle: Handle, key_name: &str, value: i64) -> Result<()> { ++pub fn set_i164(handle: &Handle, key_name: &str, value: i64) -> Result<()> { + let mut tmp = value; + let c_value: *mut c_void = &mut tmp as *mut _ as *mut c_void; + set_value(handle, key_name, DataType::Int64, c_value as *mut c_void, 8) + } + + /// Sets a string value into cmap +-pub fn set_string(handle: Handle, key_name: &str, value: &str) -> Result<()> { ++pub fn set_string(handle: &Handle, key_name: &str, value: &str) -> Result<()> { + let v_string = string_to_cstring_validated(value, 0)?; + set_value( + handle, +@@ -425,7 +449,7 @@ pub fn set_string(handle: Handle, key_name: &str, value: &str) -> Result<()> { + } + + /// Sets a binary value into cmap +-pub fn set_binary(handle: Handle, key_name: &str, value: &[u8]) -> Result<()> { ++pub fn set_binary(handle: &Handle, key_name: &str, value: &[u8]) -> Result<()> { + set_value( + handle, + key_name, +@@ -436,7 +460,7 @@ pub fn set_binary(handle: Handle, key_name: &str, value: &[u8]) -> Result<()> { + } + + /// Sets a [Data] type into cmap +-pub fn set(handle: Handle, key_name: &str, data: &Data) -> Result<()> { ++pub fn set(handle: &Handle, key_name: &str, data: &Data) -> Result<()> { + let (datatype, datalen, c_value) = match data { + Data::Int8(v) => { + let mut tmp = *v; +@@ -597,7 +621,7 @@ fn c_to_data(value_size: usize, c_key_type: u32, c_value: *const u8) -> Result Result { ++pub fn get(handle: &Handle, key_name: &str) -> Result { + let csname = string_to_cstring_validated(key_name, CMAP_KEYNAME_MAXLENGTH)?; + let mut value_size: usize = 16; + let mut c_key_type: u32 = 0; +@@ -638,7 +662,7 @@ pub fn get(handle: Handle, key_name: &str) -> Result { + } + + /// increment the value in a cmap key (must be a numeric type) +-pub fn inc(handle: Handle, key_name: &str) -> Result<()> { ++pub fn inc(handle: &Handle, key_name: &str) -> Result<()> { + let csname = string_to_cstring_validated(key_name, CMAP_KEYNAME_MAXLENGTH)?; + let res = unsafe { ffi::cmap_inc(handle.cmap_handle, csname.as_ptr()) }; + if res == ffi::CS_OK { +@@ -649,7 +673,7 @@ pub fn inc(handle: Handle, key_name: &str) -> Result<()> { + } + + /// decrement the value in a cmap key (must be a numeric type) +-pub fn dec(handle: Handle, key_name: &str) -> Result<()> { ++pub fn dec(handle: &Handle, key_name: &str) -> Result<()> { + let csname = string_to_cstring_validated(key_name, CMAP_KEYNAME_MAXLENGTH)?; + let res = unsafe { ffi::cmap_dec(handle.cmap_handle, csname.as_ptr()) }; + if res == ffi::CS_OK { +@@ -721,7 +745,7 @@ pub struct NotifyCallback { + + /// Track changes in cmap values, multiple [TrackHandle]s per [Handle] are allowed + pub fn track_add( +- handle: Handle, ++ handle: &Handle, + key_name: &str, + track_type: TrackType, + notify_callback: &NotifyCallback, +@@ -755,7 +779,7 @@ pub fn track_add( + } + + /// Remove a tracker frm this [Handle] +-pub fn track_delete(handle: Handle, track_handle: TrackHandle) -> Result<()> { ++pub fn track_delete(handle: &Handle, track_handle: TrackHandle) -> Result<()> { + let res = unsafe { ffi::cmap_track_delete(handle.cmap_handle, track_handle.track_handle) }; + if res == ffi::CS_OK { + TRACKHANDLE_HASH +@@ -864,7 +888,7 @@ impl Iterator for CmapIntoIter { + + impl CmapIterStart { + /// Create a new [CmapIterStart] object for iterating over a list of cmap keys +- pub fn new(cmap_handle: Handle, prefix: &str) -> Result { ++ pub fn new(cmap_handle: &Handle, prefix: &str) -> Result { + let mut iter_handle: u64 = 0; + let res = unsafe { + let c_prefix = string_to_cstring_validated(prefix, CMAP_KEYNAME_MAXLENGTH)?; +diff --git a/bindings/rust/src/cpg.rs b/bindings/rust/src/cpg.rs +index 12464975..36346453 100644 +--- a/bindings/rust/src/cpg.rs ++++ b/bindings/rust/src/cpg.rs +@@ -160,10 +160,35 @@ pub enum ModelData { + } + + /// A handle into the cpg library. Returned from [initialize] and needed for all other calls +-#[derive(Copy, Clone)] + pub struct Handle { + cpg_handle: u64, // Corosync library handle + model_data: ModelData, ++ clone: bool, ++} ++ ++impl Clone for Handle { ++ fn clone(&self) -> Handle { ++ Handle { ++ cpg_handle: self.cpg_handle, ++ model_data: self.model_data, ++ clone: true, ++ } ++ } ++} ++ ++impl Drop for Handle { ++ fn drop(self: &mut Handle) { ++ if !self.clone { ++ let _e = finalize(self); ++ } ++ } ++} ++ ++// Clones count as equivalent ++impl PartialEq for Handle { ++ fn eq(&self, other: &Handle) -> bool { ++ self.cpg_handle == other.cpg_handle ++ } + } + + // Used to convert a CPG handle into one of ours +@@ -329,8 +354,9 @@ pub fn initialize(model_data: &ModelData, context: u64) -> Result { + let rhandle = Handle { + cpg_handle: handle, + model_data: *model_data, ++ clone: false, + }; +- HANDLE_HASH.lock().unwrap().insert(handle, rhandle); ++ HANDLE_HASH.lock().unwrap().insert(handle, rhandle.clone()); + Ok(rhandle) + } else { + Err(CsError::from_c(res)) +@@ -339,7 +365,7 @@ pub fn initialize(model_data: &ModelData, context: u64) -> Result { + } + + /// Finish with a connection to corosync +-pub fn finalize(handle: Handle) -> Result<()> { ++pub fn finalize(handle: &Handle) -> Result<()> { + let res = unsafe { ffi::cpg_finalize(handle.cpg_handle) }; + if res == ffi::CS_OK { + HANDLE_HASH.lock().unwrap().remove(&handle.cpg_handle); +@@ -351,7 +377,7 @@ pub fn finalize(handle: Handle) -> Result<()> { + + // Not sure if an FD is the right thing to return here, but it will do for now. + /// Returns a file descriptor to use for poll/select on the CPG handle +-pub fn fd_get(handle: Handle) -> Result { ++pub fn fd_get(handle: &Handle) -> Result { + let c_fd: *mut c_int = &mut 0 as *mut _ as *mut c_int; + let res = unsafe { ffi::cpg_fd_get(handle.cpg_handle, c_fd) }; + if res == ffi::CS_OK { +@@ -362,7 +388,7 @@ pub fn fd_get(handle: Handle) -> Result { + } + + /// Call any/all active CPG callbacks for this [Handle] see [DispatchFlags] for details +-pub fn dispatch(handle: Handle, flags: DispatchFlags) -> Result<()> { ++pub fn dispatch(handle: &Handle, flags: DispatchFlags) -> Result<()> { + let res = unsafe { ffi::cpg_dispatch(handle.cpg_handle, flags as u32) }; + if res == ffi::CS_OK { + Ok(()) +@@ -372,7 +398,7 @@ pub fn dispatch(handle: Handle, flags: DispatchFlags) -> Result<()> { + } + + /// Joins a CPG group for sending and receiving messages +-pub fn join(handle: Handle, group: &str) -> Result<()> { ++pub fn join(handle: &Handle, group: &str) -> Result<()> { + let res = unsafe { + let c_group = string_to_cpg_name(group)?; + ffi::cpg_join(handle.cpg_handle, &c_group) +@@ -386,7 +412,7 @@ pub fn join(handle: Handle, group: &str) -> Result<()> { + + /// Leave the currently joined CPG group, another group can now be joined on + /// the same [Handle] or [finalize] can be called to finish using CPG +-pub fn leave(handle: Handle, group: &str) -> Result<()> { ++pub fn leave(handle: &Handle, group: &str) -> Result<()> { + let res = unsafe { + let c_group = string_to_cpg_name(group)?; + ffi::cpg_leave(handle.cpg_handle, &c_group) +@@ -399,7 +425,7 @@ pub fn leave(handle: Handle, group: &str) -> Result<()> { + } + + /// Get the local node ID +-pub fn local_get(handle: Handle) -> Result { ++pub fn local_get(handle: &Handle) -> Result { + let mut nodeid: u32 = 0; + let res = unsafe { ffi::cpg_local_get(handle.cpg_handle, &mut nodeid) }; + if res == ffi::CS_OK { +@@ -410,7 +436,7 @@ pub fn local_get(handle: Handle) -> Result { + } + + /// Get a list of members of a CPG group as a vector of [Address] structs +-pub fn membership_get(handle: Handle, group: &str) -> Result> { ++pub fn membership_get(handle: &Handle, group: &str) -> Result> { + let mut member_list_entries: i32 = 0; + let member_list = [ffi::cpg_address { + nodeid: 0, +@@ -440,7 +466,7 @@ pub fn membership_get(handle: Handle, group: &str) -> Result> { + /// Get the maximum size that CPG can send in one corosync message, + /// any messages sent via [mcast_joined] that are larger than this + /// will be fragmented +-pub fn max_atomic_msgsize_get(handle: Handle) -> Result { ++pub fn max_atomic_msgsize_get(handle: &Handle) -> Result { + let mut asize: u32 = 0; + let res = unsafe { ffi::cpg_max_atomic_msgsize_get(handle.cpg_handle, &mut asize) }; + if res == ffi::CS_OK { +@@ -453,7 +479,7 @@ pub fn max_atomic_msgsize_get(handle: Handle) -> Result { + /// Get the current 'context' value for this handle. + /// The context value is an arbitrary value that is always passed + /// back to callbacks to help identify the source +-pub fn context_get(handle: Handle) -> Result { ++pub fn context_get(handle: &Handle) -> Result { + let mut c_context: *mut c_void = &mut 0u64 as *mut _ as *mut c_void; + let (res, context) = unsafe { + let r = ffi::cpg_context_get(handle.cpg_handle, &mut c_context); +@@ -471,7 +497,7 @@ pub fn context_get(handle: Handle) -> Result { + /// The context value is an arbitrary value that is always passed + /// back to callbacks to help identify the source. + /// Normally this is set in [initialize], but this allows it to be changed +-pub fn context_set(handle: Handle, context: u64) -> Result<()> { ++pub fn context_set(handle: &Handle, context: u64) -> Result<()> { + let res = unsafe { + let c_context = context as *mut c_void; + ffi::cpg_context_set(handle.cpg_handle, c_context) +@@ -484,7 +510,7 @@ pub fn context_set(handle: Handle, context: u64) -> Result<()> { + } + + /// Get the flow control state of corosync CPG +-pub fn flow_control_state_get(handle: Handle) -> Result { ++pub fn flow_control_state_get(handle: &Handle) -> Result { + let mut fc_state: u32 = 0; + let res = unsafe { ffi::cpg_flow_control_state_get(handle.cpg_handle, &mut fc_state) }; + if res == ffi::CS_OK { +@@ -499,7 +525,7 @@ pub fn flow_control_state_get(handle: Handle) -> Result { + } + + /// Send a message to the currently joined CPG group +-pub fn mcast_joined(handle: Handle, guarantee: Guarantee, msg: &[u8]) -> Result<()> { ++pub fn mcast_joined(handle: &Handle, guarantee: Guarantee, msg: &[u8]) -> Result<()> { + let c_iovec = ffi::iovec { + iov_base: msg.as_ptr() as *mut c_void, + iov_len: msg.len(), +@@ -589,7 +615,7 @@ impl Iterator for CpgIntoIter { + + impl CpgIterStart { + /// Create a new [CpgIterStart] object for iterating over a list of active CPG groups +- pub fn new(cpg_handle: Handle, group: &str, iter_type: CpgIterType) -> Result { ++ pub fn new(cpg_handle: &Handle, group: &str, iter_type: CpgIterType) -> Result { + let mut iter_handle: u64 = 0; + let res = unsafe { + let mut c_group = string_to_cpg_name(group)?; +diff --git a/bindings/rust/src/quorum.rs b/bindings/rust/src/quorum.rs +index 25c2fe62..d20f1d64 100644 +--- a/bindings/rust/src/quorum.rs ++++ b/bindings/rust/src/quorum.rs +@@ -137,10 +137,35 @@ pub struct Model1Data { + } + + /// A handle into the quorum library. Returned from [initialize] and needed for all other calls +-#[derive(Copy, Clone)] + pub struct Handle { + quorum_handle: u64, + model_data: ModelData, ++ clone: bool, ++} ++ ++impl Clone for Handle { ++ fn clone(&self) -> Handle { ++ Handle { ++ quorum_handle: self.quorum_handle, ++ model_data: self.model_data, ++ clone: true, ++ } ++ } ++} ++ ++impl Drop for Handle { ++ fn drop(self: &mut Handle) { ++ if !self.clone { ++ let _e = finalize(self); ++ } ++ } ++} ++ ++// Clones count as equivalent ++impl PartialEq for Handle { ++ fn eq(&self, other: &Handle) -> bool { ++ self.quorum_handle == other.quorum_handle ++ } + } + + /// Initialize a connection to the quorum library. You must call this before doing anything +@@ -187,13 +212,14 @@ pub fn initialize(model_data: &ModelData, context: u64) -> Result<(Handle, Quoru + let rhandle = Handle { + quorum_handle: handle, + model_data: *model_data, ++ clone: false, + }; +- HANDLE_HASH.lock().unwrap().insert(handle, rhandle); ++ HANDLE_HASH.lock().unwrap().insert(handle, rhandle.clone()); + Ok((rhandle, quorum_type)) + } + + /// Finish with a connection to corosync +-pub fn finalize(handle: Handle) -> Result<()> { ++pub fn finalize(handle: &Handle) -> Result<()> { + let res = unsafe { ffi::quorum_finalize(handle.quorum_handle) }; + if res == ffi::CS_OK { + HANDLE_HASH.lock().unwrap().remove(&handle.quorum_handle); +@@ -205,7 +231,7 @@ pub fn finalize(handle: Handle) -> Result<()> { + + // Not sure if an FD is the right thing to return here, but it will do for now. + /// Return a file descriptor to use for poll/select on the QUORUM handle +-pub fn fd_get(handle: Handle) -> Result { ++pub fn fd_get(handle: &Handle) -> Result { + let c_fd: *mut c_int = &mut 0 as *mut _ as *mut c_int; + let res = unsafe { ffi::quorum_fd_get(handle.quorum_handle, c_fd) }; + if res == ffi::CS_OK { +@@ -216,7 +242,7 @@ pub fn fd_get(handle: Handle) -> Result { + } + + /// Display any/all active QUORUM callbacks for this [Handle], see [DispatchFlags] for details +-pub fn dispatch(handle: Handle, flags: DispatchFlags) -> Result<()> { ++pub fn dispatch(handle: &Handle, flags: DispatchFlags) -> Result<()> { + let res = unsafe { ffi::quorum_dispatch(handle.quorum_handle, flags as u32) }; + if res == ffi::CS_OK { + Ok(()) +@@ -226,7 +252,7 @@ pub fn dispatch(handle: Handle, flags: DispatchFlags) -> Result<()> { + } + + /// Return the quorate status of the cluster +-pub fn getquorate(handle: Handle) -> Result { ++pub fn getquorate(handle: &Handle) -> Result { + let c_quorate: *mut c_int = &mut 0 as *mut _ as *mut c_int; + let (res, r_quorate) = unsafe { + let res = ffi::quorum_getquorate(handle.quorum_handle, c_quorate); +@@ -245,7 +271,7 @@ pub fn getquorate(handle: Handle) -> Result { + } + + /// Track node and quorum changes +-pub fn trackstart(handle: Handle, flags: TrackFlags) -> Result<()> { ++pub fn trackstart(handle: &Handle, flags: TrackFlags) -> Result<()> { + let res = unsafe { ffi::quorum_trackstart(handle.quorum_handle, flags as u32) }; + if res == ffi::CS_OK { + Ok(()) +@@ -255,7 +281,7 @@ pub fn trackstart(handle: Handle, flags: TrackFlags) -> Result<()> { + } + + /// Stop tracking node and quorum changes +-pub fn trackstop(handle: Handle) -> Result<()> { ++pub fn trackstop(handle: &Handle) -> Result<()> { + let res = unsafe { ffi::quorum_trackstop(handle.quorum_handle) }; + if res == ffi::CS_OK { + Ok(()) +@@ -267,7 +293,7 @@ pub fn trackstop(handle: Handle) -> Result<()> { + /// Get the current 'context' value for this handle. + /// The context value is an arbitrary value that is always passed + /// back to callbacks to help identify the source +-pub fn context_get(handle: Handle) -> Result { ++pub fn context_get(handle: &Handle) -> Result { + let (res, context) = unsafe { + let mut context: u64 = 0; + let c_context: *mut c_void = &mut context as *mut _ as *mut c_void; +@@ -285,7 +311,7 @@ pub fn context_get(handle: Handle) -> Result { + /// The context value is an arbitrary value that is always passed + /// back to callbacks to help identify the source. + /// Normally this is set in [initialize], but this allows it to be changed +-pub fn context_set(handle: Handle, context: u64) -> Result<()> { ++pub fn context_set(handle: &Handle, context: u64) -> Result<()> { + let res = unsafe { + let c_context = context as *mut c_void; + ffi::quorum_context_set(handle.quorum_handle, c_context) +diff --git a/bindings/rust/src/votequorum.rs b/bindings/rust/src/votequorum.rs +index 4718b586..1e34fcac 100644 +--- a/bindings/rust/src/votequorum.rs ++++ b/bindings/rust/src/votequorum.rs +@@ -189,10 +189,35 @@ pub struct Callbacks { + } + + /// A handle into the votequorum library. Returned from [initialize] and needed for all other calls +-#[derive(Copy, Clone)] + pub struct Handle { + votequorum_handle: u64, + callbacks: Callbacks, ++ clone: bool, ++} ++ ++impl Clone for Handle { ++ fn clone(&self) -> Handle { ++ Handle { ++ votequorum_handle: self.votequorum_handle, ++ callbacks: self.callbacks, ++ clone: true, ++ } ++ } ++} ++ ++impl Drop for Handle { ++ fn drop(self: &mut Handle) { ++ if !self.clone { ++ let _e = finalize(self); ++ } ++ } ++} ++ ++// Clones count as equivalent ++impl PartialEq for Handle { ++ fn eq(&self, other: &Handle) -> bool { ++ self.votequorum_handle == other.votequorum_handle ++ } + } + + /// Initialize a connection to the votequorum library. You must call this before doing anything +@@ -213,8 +238,9 @@ pub fn initialize(callbacks: &Callbacks) -> Result { + let rhandle = Handle { + votequorum_handle: handle, + callbacks: *callbacks, ++ clone: false, + }; +- HANDLE_HASH.lock().unwrap().insert(handle, rhandle); ++ HANDLE_HASH.lock().unwrap().insert(handle, rhandle.clone()); + Ok(rhandle) + } else { + Err(CsError::from_c(res)) +@@ -223,7 +249,7 @@ pub fn initialize(callbacks: &Callbacks) -> Result { + } + + /// Finish with a connection to corosync +-pub fn finalize(handle: Handle) -> Result<()> { ++pub fn finalize(handle: &Handle) -> Result<()> { + let res = unsafe { ffi::votequorum_finalize(handle.votequorum_handle) }; + if res == ffi::CS_OK { + HANDLE_HASH +@@ -238,7 +264,7 @@ pub fn finalize(handle: Handle) -> Result<()> { + + // Not sure if an FD is the right thing to return here, but it will do for now. + /// Return a file descriptor to use for poll/select on the VOTEQUORUM handle +-pub fn fd_get(handle: Handle) -> Result { ++pub fn fd_get(handle: &Handle) -> Result { + let c_fd: *mut c_int = &mut 0 as *mut _ as *mut c_int; + let res = unsafe { ffi::votequorum_fd_get(handle.votequorum_handle, c_fd) }; + if res == ffi::CS_OK { +@@ -251,7 +277,7 @@ pub fn fd_get(handle: Handle) -> Result { + const VOTEQUORUM_QDEVICE_MAX_NAME_LEN: usize = 255; + + /// Returns detailed information about a node in a [NodeInfo] structure +-pub fn get_info(handle: Handle, nodeid: NodeId) -> Result { ++pub fn get_info(handle: &Handle, nodeid: NodeId) -> Result { + let mut c_info = ffi::votequorum_info { + node_id: 0, + node_state: 0, +@@ -293,7 +319,7 @@ pub fn get_info(handle: Handle, nodeid: NodeId) -> Result { + } + + /// Call any/all active votequorum callbacks for this [Handle]. see [DispatchFlags] for details +-pub fn dispatch(handle: Handle, flags: DispatchFlags) -> Result<()> { ++pub fn dispatch(handle: &Handle, flags: DispatchFlags) -> Result<()> { + let res = unsafe { ffi::votequorum_dispatch(handle.votequorum_handle, flags as u32) }; + if res == ffi::CS_OK { + Ok(()) +@@ -303,7 +329,7 @@ pub fn dispatch(handle: Handle, flags: DispatchFlags) -> Result<()> { + } + + /// Track node and votequorum changes +-pub fn trackstart(handle: Handle, context: u64, flags: TrackFlags) -> Result<()> { ++pub fn trackstart(handle: &Handle, context: u64, flags: TrackFlags) -> Result<()> { + let res = + unsafe { ffi::votequorum_trackstart(handle.votequorum_handle, context, flags as u32) }; + if res == ffi::CS_OK { +@@ -314,7 +340,7 @@ pub fn trackstart(handle: Handle, context: u64, flags: TrackFlags) -> Result<()> + } + + /// Stop tracking node and votequorum changes +-pub fn trackstop(handle: Handle) -> Result<()> { ++pub fn trackstop(handle: &Handle) -> Result<()> { + let res = unsafe { ffi::votequorum_trackstop(handle.votequorum_handle) }; + if res == ffi::CS_OK { + Ok(()) +@@ -326,7 +352,7 @@ pub fn trackstop(handle: Handle) -> Result<()> { + /// Get the current 'context' value for this handle. + /// The context value is an arbitrary value that is always passed + /// back to callbacks to help identify the source +-pub fn context_get(handle: Handle) -> Result { ++pub fn context_get(handle: &Handle) -> Result { + let (res, context) = unsafe { + let mut c_context: *mut c_void = &mut 0u64 as *mut _ as *mut c_void; + let r = ffi::votequorum_context_get(handle.votequorum_handle, &mut c_context); +@@ -344,7 +370,7 @@ pub fn context_get(handle: Handle) -> Result { + /// The context value is an arbitrary value that is always passed + /// back to callbacks to help identify the source. + /// Normally this is set in [trackstart], but this allows it to be changed +-pub fn context_set(handle: Handle, context: u64) -> Result<()> { ++pub fn context_set(handle: &Handle, context: u64) -> Result<()> { + let res = unsafe { + let c_context = context as *mut c_void; + ffi::votequorum_context_set(handle.votequorum_handle, c_context) +@@ -358,7 +384,7 @@ pub fn context_set(handle: Handle, context: u64) -> Result<()> { + + /// Set the current expected_votes for the cluster, this value must + /// be valid and not result in an inquorate cluster. +-pub fn set_expected(handle: Handle, expected_votes: u32) -> Result<()> { ++pub fn set_expected(handle: &Handle, expected_votes: u32) -> Result<()> { + let res = unsafe { ffi::votequorum_setexpected(handle.votequorum_handle, expected_votes) }; + if res == ffi::CS_OK { + Ok(()) +@@ -368,7 +394,7 @@ pub fn set_expected(handle: Handle, expected_votes: u32) -> Result<()> { + } + + /// Set the current votes for a node +-pub fn set_votes(handle: Handle, nodeid: NodeId, votes: u32) -> Result<()> { ++pub fn set_votes(handle: &Handle, nodeid: NodeId, votes: u32) -> Result<()> { + let res = + unsafe { ffi::votequorum_setvotes(handle.votequorum_handle, u32::from(nodeid), votes) }; + if res == ffi::CS_OK { +@@ -379,7 +405,7 @@ pub fn set_votes(handle: Handle, nodeid: NodeId, votes: u32) -> Result<()> { + } + + /// Register a quorum device +-pub fn qdevice_register(handle: Handle, name: &str) -> Result<()> { ++pub fn qdevice_register(handle: &Handle, name: &str) -> Result<()> { + let c_string = { + match CString::new(name) { + Ok(cs) => cs, +@@ -397,7 +423,7 @@ pub fn qdevice_register(handle: Handle, name: &str) -> Result<()> { + } + + /// Unregister a quorum device +-pub fn qdevice_unregister(handle: Handle, name: &str) -> Result<()> { ++pub fn qdevice_unregister(handle: &Handle, name: &str) -> Result<()> { + let c_string = { + match CString::new(name) { + Ok(cs) => cs, +@@ -415,7 +441,7 @@ pub fn qdevice_unregister(handle: Handle, name: &str) -> Result<()> { + } + + /// Update the name of a quorum device +-pub fn qdevice_update(handle: Handle, oldname: &str, newname: &str) -> Result<()> { ++pub fn qdevice_update(handle: &Handle, oldname: &str, newname: &str) -> Result<()> { + let on_string = { + match CString::new(oldname) { + Ok(cs) => cs, +@@ -446,7 +472,7 @@ pub fn qdevice_update(handle: Handle, oldname: &str, newname: &str) -> Result<() + /// Poll a quorum device + /// This must be done more often than the qdevice timeout (default 10s) while the device is active + /// and the [RingId] must match the current value returned from the callbacks for it to be accepted. +-pub fn qdevice_poll(handle: Handle, name: &str, cast_vote: bool, ring_id: &RingId) -> Result<()> { ++pub fn qdevice_poll(handle: &Handle, name: &str, cast_vote: bool, ring_id: &RingId) -> Result<()> { + let c_string = { + match CString::new(name) { + Ok(cs) => cs, +@@ -476,7 +502,7 @@ pub fn qdevice_poll(handle: Handle, name: &str, cast_vote: bool, ring_id: &RingI + } + + /// Allow qdevice to tell votequorum if master_wins can be enabled or not +-pub fn qdevice_master_wins(handle: Handle, name: &str, master_wins: bool) -> Result<()> { ++pub fn qdevice_master_wins(handle: &Handle, name: &str, master_wins: bool) -> Result<()> { + let c_string = { + match CString::new(name) { + Ok(cs) => cs, +diff --git a/bindings/rust/tests/src/bin/cfg-test.rs b/bindings/rust/tests/src/bin/cfg-test.rs +index cd70d388..1b245251 100644 +--- a/bindings/rust/tests/src/bin/cfg-test.rs ++++ b/bindings/rust/tests/src/bin/cfg-test.rs +@@ -5,7 +5,7 @@ use corosync::{cfg, NodeId}; + + use std::thread::spawn; + +-fn dispatch_thread(handle: cfg::Handle) { ++fn dispatch_thread(handle: &cfg::Handle) { + loop { + if cfg::dispatch(handle, corosync::DispatchFlags::One).is_err() { + return; +@@ -18,7 +18,7 @@ fn shutdown_check_fn(handle: &cfg::Handle, _flags: u32) { + println!("in shutdown callback"); + + // DON'T shutdown corosync - we're just testing +- if let Err(e) = cfg::reply_to_shutdown(*handle, cfg::ShutdownReply::No) { ++ if let Err(e) = cfg::reply_to_shutdown(handle, cfg::ShutdownReply::No) { + println!("Error in CFG replyto_shutdown: {e}"); + } + } +@@ -52,10 +52,10 @@ fn main() { + } + }; + +- match cfg::track_start(handle2, cfg::TrackFlags::None) { ++ match cfg::track_start(&handle2, cfg::TrackFlags::None) { + Ok(_) => { + // Run handle2 dispatch in its own thread +- spawn(move || dispatch_thread(handle2)); ++ spawn(move || dispatch_thread(&handle2)); + } + Err(e) => { + println!("Error in CFG track_start: {e}"); +@@ -63,7 +63,7 @@ fn main() { + }; + + let local_nodeid = { +- match cfg::local_get(handle) { ++ match cfg::local_get(&handle) { + Ok(n) => { + println!("Local nodeid is {n}"); + Some(n) +@@ -82,10 +82,10 @@ fn main() { + if let Some(our_nodeid) = local_nodeid { + let us_plus1 = NodeId::from(u32::from(our_nodeid) + 1); + let us_less1 = NodeId::from(u32::from(our_nodeid) - 1); +- let mut res = cfg::node_status_get(handle, us_plus1, cfg::NodeStatusVersion::V1); ++ let mut res = cfg::node_status_get(&handle, us_plus1, cfg::NodeStatusVersion::V1); + if let Err(e) = res { + println!("Error from node_status_get on nodeid {us_plus1}: {e}"); +- res = cfg::node_status_get(handle, us_less1, cfg::NodeStatusVersion::V1); ++ res = cfg::node_status_get(&handle, us_less1, cfg::NodeStatusVersion::V1); + }; + match res { + Ok(ns) => { +@@ -113,8 +113,8 @@ fn main() { + } + } + +- // This should not shutdown corosync because the callback on handle2 will refuse it. +- match cfg::try_shutdown(handle, cfg::ShutdownFlags::Request) { ++ // This should not shutdown corosync because the callback on &handle2 will refuse it. ++ match cfg::try_shutdown(&handle, cfg::ShutdownFlags::Request) { + Ok(_) => { + println!("CFG try_shutdown suceeded, should return busy"); + } +@@ -127,7 +127,7 @@ fn main() { + + // Wait for events + loop { +- if cfg::dispatch(handle, corosync::DispatchFlags::One).is_err() { ++ if cfg::dispatch(&handle, corosync::DispatchFlags::One).is_err() { + break; + } + } +diff --git a/bindings/rust/tests/src/bin/cmap-test.rs b/bindings/rust/tests/src/bin/cmap-test.rs +index f4356536..25ec722a 100644 +--- a/bindings/rust/tests/src/bin/cmap-test.rs ++++ b/bindings/rust/tests/src/bin/cmap-test.rs +@@ -31,47 +31,47 @@ fn main() { + }; + + // Test some SETs +- if let Err(e) = cmap::set_u32(handle, "test.test_uint32", 456) { ++ if let Err(e) = cmap::set_u32(&handle, "test.test_uint32", 456) { + println!("Error in CMAP set_u32: {e}"); + return; + }; + +- if let Err(e) = cmap::set_i16(handle, "test.test_int16", -789) { ++ if let Err(e) = cmap::set_i16(&handle, "test.test_int16", -789) { + println!("Error in CMAP set_i16: {e}"); + return; + }; + +- if let Err(e) = cmap::set_number(handle, "test.test_num_1", 6809u32) { ++ if let Err(e) = cmap::set_number(&handle, "test.test_num_1", 6809u32) { + println!("Error in CMAP set_number(u32): {e}"); + return; + }; + + // NOT PI (just to avoid clippy whingeing) +- if let Err(e) = cmap::set_number(handle, "test.test_num_2", 3.24159265) { ++ if let Err(e) = cmap::set_number(&handle, "test.test_num_2", 3.24159265) { + println!("Error in CMAP set_number(f32): {e}"); + return; + }; + +- if let Err(e) = cmap::set_string(handle, "test.test_string", "Hello from Rust") { ++ if let Err(e) = cmap::set_string(&handle, "test.test_string", "Hello from Rust") { + println!("Error in CMAP set_string: {e}"); + return; + }; + + let test_d = cmap::Data::UInt64(0xdeadbeefbacecafe); +- if let Err(e) = cmap::set(handle, "test.test_data", &test_d) { ++ if let Err(e) = cmap::set(&handle, "test.test_data", &test_d) { + println!("Error in CMAP set_data: {e}"); + return; + }; + + // let test_d2 = cmap::Data::UInt32(6809); + let test_d2 = cmap::Data::String("Test string in data 12345".to_string()); +- if let Err(e) = cmap::set(handle, "test.test_again", &test_d2) { ++ if let Err(e) = cmap::set(&handle, "test.test_again", &test_d2) { + println!("Error in CMAP set_data2: {e}"); + return; + }; + + // get them back again +- match cmap::get(handle, "test.test_uint32") { ++ match cmap::get(&handle, "test.test_uint32") { + Ok(v) => { + println!("GOT uint32 {v}"); + } +@@ -81,7 +81,7 @@ fn main() { + return; + } + }; +- match cmap::get(handle, "test.test_int16") { ++ match cmap::get(&handle, "test.test_int16") { + Ok(v) => { + println!("GOT uint16 {v}"); + } +@@ -92,7 +92,7 @@ fn main() { + } + }; + +- match cmap::get(handle, "test.test_num_1") { ++ match cmap::get(&handle, "test.test_num_1") { + Ok(v) => { + println!("GOT num {v}"); + } +@@ -102,7 +102,7 @@ fn main() { + return; + } + }; +- match cmap::get(handle, "test.test_num_2") { ++ match cmap::get(&handle, "test.test_num_2") { + Ok(v) => { + println!("GOT num {v}"); + } +@@ -112,7 +112,7 @@ fn main() { + return; + } + }; +- match cmap::get(handle, "test.test_string") { ++ match cmap::get(&handle, "test.test_string") { + Ok(v) => { + println!("GOT string {v}"); + } +@@ -123,7 +123,7 @@ fn main() { + } + }; + +- match cmap::get(handle, "test.test_data") { ++ match cmap::get(&handle, "test.test_data") { + Ok(v) => match v { + cmap::Data::UInt64(u) => println!("GOT data value {u:x}"), + _ => println!("ERROR type was not UInt64, got {v}"), +@@ -136,7 +136,7 @@ fn main() { + }; + + // Test an iterator +- match cmap::CmapIterStart::new(handle, "totem.") { ++ match cmap::CmapIterStart::new(&handle, "totem.") { + Ok(cmap_iter) => { + for i in cmap_iter { + println!("ITER: {i:?}"); +@@ -149,7 +149,7 @@ fn main() { + } + + // Close this handle +- if let Err(e) = cmap::finalize(handle) { ++ if let Err(e) = cmap::finalize(&handle) { + println!("Error in CMAP get: {e}"); + return; + }; +@@ -167,7 +167,7 @@ fn main() { + notify_fn: Some(track_notify_fn), + }; + let _track_handle = match cmap::track_add( +- handle, ++ &handle, + "stats.srp.memb_merge_detect_tx", + cmap::TrackType::MODIFY | cmap::TrackType::ADD | cmap::TrackType::DELETE, + &cb, +@@ -183,7 +183,7 @@ fn main() { + // Wait for events + let mut event_num = 0; + loop { +- if let Err(e) = cmap::dispatch(handle, corosync::DispatchFlags::One) { ++ if let Err(e) = cmap::dispatch(&handle, corosync::DispatchFlags::One) { + println!("Error from CMAP dispatch: {e}"); + } + // Just do 5 +diff --git a/bindings/rust/tests/src/bin/cpg-test.rs b/bindings/rust/tests/src/bin/cpg-test.rs +index df83c2d5..c5a58edc 100644 +--- a/bindings/rust/tests/src/bin/cpg-test.rs ++++ b/bindings/rust/tests/src/bin/cpg-test.rs +@@ -66,12 +66,12 @@ fn main() { + } + }; + +- if let Err(e) = cpg::join(handle, "TEST") { ++ if let Err(e) = cpg::join(&handle, "TEST") { + println!("Error in CPG join: {e}"); + return; + } + +- match cpg::local_get(handle) { ++ match cpg::local_get(&handle) { + Ok(n) => { + println!("Local nodeid is {n}"); + } +@@ -81,7 +81,7 @@ fn main() { + } + + // Test membership_get() +- match cpg::membership_get(handle, "TEST") { ++ match cpg::membership_get(&handle, "TEST") { + Ok(m) => { + println!(" members: {m:?}"); + println!(); +@@ -93,13 +93,13 @@ fn main() { + + // Test context APIs + let set_context: u64 = 0xabcdbeefcafe; +- if let Err(e) = cpg::context_set(handle, set_context) { ++ if let Err(e) = cpg::context_set(&handle, set_context) { + println!("Error in CPG context_set: {e}"); + return; + } + + // NOTE This will fail on 32 bit systems because void* is not u64 +- match cpg::context_get(handle) { ++ match cpg::context_get(&handle) { + Ok(c) => { + if c != set_context { + println!("Error: context_get() returned {c:x}, context should be {set_context:x}"); +@@ -111,7 +111,7 @@ fn main() { + } + + // Test iterator +- match cpg::CpgIterStart::new(handle, "", cpg::CpgIterType::All) { ++ match cpg::CpgIterStart::new(&handle, "", cpg::CpgIterType::All) { + Ok(cpg_iter) => { + for i in cpg_iter { + println!("ITER: {i:?}"); +@@ -125,7 +125,7 @@ fn main() { + + // We should receive our own message (at least) in the event loop + if let Err(e) = cpg::mcast_joined( +- handle, ++ &handle, + cpg::Guarantee::TypeAgreed, + &"This is a test".to_string().into_bytes(), + ) { +@@ -134,7 +134,7 @@ fn main() { + + // Wait for events + loop { +- if cpg::dispatch(handle, corosync::DispatchFlags::One).is_err() { ++ if cpg::dispatch(&handle, corosync::DispatchFlags::One).is_err() { + break; + } + } +diff --git a/bindings/rust/tests/src/bin/quorum-test.rs b/bindings/rust/tests/src/bin/quorum-test.rs +index 5797b7d0..9436b392 100644 +--- a/bindings/rust/tests/src/bin/quorum-test.rs ++++ b/bindings/rust/tests/src/bin/quorum-test.rs +@@ -51,13 +51,13 @@ fn main() { + + // Test context APIs + let set_context: u64 = 0xabcdbeefcafe; +- if let Err(e) = quorum::context_set(handle, set_context) { ++ if let Err(e) = quorum::context_set(&handle, set_context) { + println!("Error in QUORUM context_set: {e}"); + return; + } + + // NOTE This will fail on 32 bit systems because void* is not u64 +- match quorum::context_get(handle) { ++ match quorum::context_get(&handle) { + Ok(c) => { + if c != set_context { + println!("Error: context_get() returned {c:x}, context should be {set_context:x}"); +@@ -68,14 +68,14 @@ fn main() { + } + } + +- if let Err(e) = quorum::trackstart(handle, corosync::TrackFlags::Changes) { ++ if let Err(e) = quorum::trackstart(&handle, corosync::TrackFlags::Changes) { + println!("Error in QUORUM trackstart: {e}"); + return; + } + + // Wait for events + loop { +- if quorum::dispatch(handle, corosync::DispatchFlags::One).is_err() { ++ if quorum::dispatch(&handle, corosync::DispatchFlags::One).is_err() { + break; + } + } +diff --git a/bindings/rust/tests/src/bin/votequorum-test.rs b/bindings/rust/tests/src/bin/votequorum-test.rs +index cf9746b6..eebc9f0d 100644 +--- a/bindings/rust/tests/src/bin/votequorum-test.rs ++++ b/bindings/rust/tests/src/bin/votequorum-test.rs +@@ -51,12 +51,12 @@ fn main() { + + // Test context APIs + let set_context: u64 = 0xabcdbeefcafe; +- if let Err(e) = votequorum::context_set(handle, set_context) { ++ if let Err(e) = votequorum::context_set(&handle, set_context) { + println!("Error in VOTEQUORUM context_set: {e}"); + } + + // NOTE This will fail on 32 bit systems because void* is not u64 +- match votequorum::context_get(handle) { ++ match votequorum::context_get(&handle) { + Ok(c) => { + if c != set_context { + println!("Error: context_get() returned {c:x}, context should be {set_context:x}"); +@@ -69,11 +69,11 @@ fn main() { + + const QDEVICE_NAME: &str = "RustQdevice"; + +- if let Err(e) = votequorum::qdevice_register(handle, QDEVICE_NAME) { ++ if let Err(e) = votequorum::qdevice_register(&handle, QDEVICE_NAME) { + println!("Error in VOTEQUORUM qdevice_register: {e}"); + } + +- match votequorum::get_info(handle, corosync::NodeId::from(1u32)) { ++ match votequorum::get_info(&handle, corosync::NodeId::from(1u32)) { + Ok(i) => { + println!("Node info for nodeid 1"); + println!(" nodeid: {}", i.node_id); +@@ -98,18 +98,18 @@ fn main() { + } + } + +- if let Err(e) = votequorum::qdevice_unregister(handle, QDEVICE_NAME) { ++ if let Err(e) = votequorum::qdevice_unregister(&handle, QDEVICE_NAME) { + println!("Error in VOTEQUORUM qdevice_unregister: {e}"); + } + +- if let Err(e) = votequorum::trackstart(handle, 99_u64, corosync::TrackFlags::Changes) { ++ if let Err(e) = votequorum::trackstart(&handle, 99_u64, corosync::TrackFlags::Changes) { + println!("Error in VOTEQUORUM trackstart: {e}"); + return; + } + + // Wait for events + loop { +- if votequorum::dispatch(handle, corosync::DispatchFlags::One).is_err() { ++ if votequorum::dispatch(&handle, corosync::DispatchFlags::One).is_err() { + break; + } + } +-- +2.25.1 + diff --git a/backport-rust-tests-return-errors-and-don-t-hang.patch b/backport-rust-tests-return-errors-and-don-t-hang.patch new file mode 100644 index 0000000..f6631e2 --- /dev/null +++ b/backport-rust-tests-return-errors-and-don-t-hang.patch @@ -0,0 +1,494 @@ +From b98248d9a53726aaa13229f02d97c9b60246beb9 Mon Sep 17 00:00:00 2001 +From: Christine Caulfield +Date: Mon, 8 Jan 2024 14:56:37 +0000 +Subject: [PATCH] rust: tests return errors and don't hang + +Signed-off-by: Christine Caulfield +Reviewed-by: Jan Friesse +--- + bindings/rust/tests/src/bin/cfg-test.rs | 22 ++++++----- + bindings/rust/tests/src/bin/cmap-test.rs | 39 ++++++++++--------- + bindings/rust/tests/src/bin/cpg-test.rs | 24 +++++++----- + bindings/rust/tests/src/bin/quorum-test.rs | 21 +++++----- + .../rust/tests/src/bin/votequorum-test.rs | 23 ++++++----- + 5 files changed, 71 insertions(+), 58 deletions(-) + +diff --git a/bindings/rust/tests/src/bin/cfg-test.rs b/bindings/rust/tests/src/bin/cfg-test.rs +index 1b245251..5b6f9ce0 100644 +--- a/bindings/rust/tests/src/bin/cfg-test.rs ++++ b/bindings/rust/tests/src/bin/cfg-test.rs +@@ -23,7 +23,7 @@ fn shutdown_check_fn(handle: &cfg::Handle, _flags: u32) { + } + } + +-fn main() { ++fn main() -> Result<(), corosync::CsError> { + // Initialise the callbacks data + let cb = cfg::Callbacks { + corosync_cfg_shutdown_callback_fn: Some(shutdown_check_fn), +@@ -36,7 +36,7 @@ fn main() { + } + Err(e) => { + println!("Error in CFG init: {e}"); +- return; ++ return Err(e); + } + }; + +@@ -48,7 +48,7 @@ fn main() { + } + Err(e) => { + println!("Error in CFG init: {e}"); +- return; ++ return Err(e); + } + }; + +@@ -59,6 +59,7 @@ fn main() { + } + Err(e) => { + println!("Error in CFG track_start: {e}"); ++ return Err(e); + } + }; + +@@ -70,7 +71,7 @@ fn main() { + } + Err(e) => { + println!("Error in CFG local_get: {e}"); +- None ++ return Err(e); + } + } + }; +@@ -109,6 +110,7 @@ fn main() { + println!( + "Error in CFG node_status get: {e} (tried nodeids {us_plus1} & {us_less1})" + ); ++ return Err(e); + } + } + } +@@ -121,15 +123,15 @@ fn main() { + Err(e) => { + if e != corosync::CsError::CsErrBusy { + println!("Error in CFG try_shutdown: {e}"); ++ return Err(e); + } + } + } + +- // Wait for events +- loop { +- if cfg::dispatch(&handle, corosync::DispatchFlags::One).is_err() { +- break; +- } ++ // Quick test of dispatch ++ if let Err(e) = cfg::dispatch(&handle, corosync::DispatchFlags::OneNonblocking) { ++ println!("Error in CFG dispatch"); ++ return Err(e); + } +- println!("ERROR: Corosync quit"); ++ Ok(()) + } +diff --git a/bindings/rust/tests/src/bin/cmap-test.rs b/bindings/rust/tests/src/bin/cmap-test.rs +index 25ec722a..edc0305a 100644 +--- a/bindings/rust/tests/src/bin/cmap-test.rs ++++ b/bindings/rust/tests/src/bin/cmap-test.rs +@@ -18,7 +18,7 @@ fn track_notify_fn( + println!(" New value: {new_value}"); + } + +-fn main() { ++fn main() -> Result<(), corosync::CsError> { + let handle = match cmap::initialize(cmap::Map::Icmap) { + Ok(h) => { + println!("cmap initialized."); +@@ -26,48 +26,48 @@ fn main() { + } + Err(e) => { + println!("Error in CMAP (Icmap) init: {e}"); +- return; ++ return Err(e); + } + }; + + // Test some SETs + if let Err(e) = cmap::set_u32(&handle, "test.test_uint32", 456) { + println!("Error in CMAP set_u32: {e}"); +- return; ++ return Err(e); + }; + + if let Err(e) = cmap::set_i16(&handle, "test.test_int16", -789) { + println!("Error in CMAP set_i16: {e}"); +- return; ++ return Err(e); + }; + + if let Err(e) = cmap::set_number(&handle, "test.test_num_1", 6809u32) { + println!("Error in CMAP set_number(u32): {e}"); +- return; ++ return Err(e); + }; + + // NOT PI (just to avoid clippy whingeing) + if let Err(e) = cmap::set_number(&handle, "test.test_num_2", 3.24159265) { + println!("Error in CMAP set_number(f32): {e}"); +- return; ++ return Err(e); + }; + + if let Err(e) = cmap::set_string(&handle, "test.test_string", "Hello from Rust") { + println!("Error in CMAP set_string: {e}"); +- return; ++ return Err(e); + }; + + let test_d = cmap::Data::UInt64(0xdeadbeefbacecafe); + if let Err(e) = cmap::set(&handle, "test.test_data", &test_d) { + println!("Error in CMAP set_data: {e}"); +- return; ++ return Err(e); + }; + + // let test_d2 = cmap::Data::UInt32(6809); + let test_d2 = cmap::Data::String("Test string in data 12345".to_string()); + if let Err(e) = cmap::set(&handle, "test.test_again", &test_d2) { + println!("Error in CMAP set_data2: {e}"); +- return; ++ return Err(e); + }; + + // get them back again +@@ -78,7 +78,7 @@ fn main() { + + Err(e) => { + println!("Error in CMAP get: {e}"); +- return; ++ return Err(e); + } + }; + match cmap::get(&handle, "test.test_int16") { +@@ -88,7 +88,7 @@ fn main() { + + Err(e) => { + println!("Error in CMAP get: {e}"); +- return; ++ return Err(e); + } + }; + +@@ -99,7 +99,7 @@ fn main() { + + Err(e) => { + println!("Error in CMAP get: {e}"); +- return; ++ return Err(e); + } + }; + match cmap::get(&handle, "test.test_num_2") { +@@ -109,7 +109,7 @@ fn main() { + + Err(e) => { + println!("Error in CMAP get: {e}"); +- return; ++ return Err(e); + } + }; + match cmap::get(&handle, "test.test_string") { +@@ -119,7 +119,7 @@ fn main() { + + Err(e) => { + println!("Error in CMAP get: {e}"); +- return; ++ return Err(e); + } + }; + +@@ -131,7 +131,7 @@ fn main() { + + Err(e) => { + println!("Error in CMAP get: {e}"); +- return; ++ return Err(e); + } + }; + +@@ -151,7 +151,7 @@ fn main() { + // Close this handle + if let Err(e) = cmap::finalize(&handle) { + println!("Error in CMAP get: {e}"); +- return; ++ return Err(e); + }; + + // Test notifications on the stats map +@@ -159,7 +159,7 @@ fn main() { + Ok(h) => h, + Err(e) => { + println!("Error in CMAP (Stats) init: {e}"); +- return; ++ return Err(e); + } + }; + +@@ -176,11 +176,11 @@ fn main() { + Ok(th) => th, + Err(e) => { + println!("Error in CMAP track_add {e}"); +- return; ++ return Err(e); + } + }; + +- // Wait for events ++ // Wait for some events + let mut event_num = 0; + loop { + if let Err(e) = cmap::dispatch(&handle, corosync::DispatchFlags::One) { +@@ -192,4 +192,5 @@ fn main() { + break; + } + } ++ Ok(()) + } +diff --git a/bindings/rust/tests/src/bin/cpg-test.rs b/bindings/rust/tests/src/bin/cpg-test.rs +index c5a58edc..dd574940 100644 +--- a/bindings/rust/tests/src/bin/cpg-test.rs ++++ b/bindings/rust/tests/src/bin/cpg-test.rs +@@ -49,7 +49,7 @@ fn totem_confchg_fn(_handle: &cpg::Handle, ring_id: cpg::RingId, member_list: Ve + println!(" members: {member_list:?}"); + } + +-fn main() { ++fn main() -> Result<(), corosync::CsError> { + // Initialise the model data + let md = cpg::ModelData::ModelV1(cpg::Model1Data { + flags: cpg::Model1Flags::None, +@@ -62,13 +62,13 @@ fn main() { + Ok(h) => h, + Err(e) => { + println!("Error in CPG init: {e}"); +- return; ++ return Err(e); + } + }; + + if let Err(e) = cpg::join(&handle, "TEST") { + println!("Error in CPG join: {e}"); +- return; ++ return Err(e); + } + + match cpg::local_get(&handle) { +@@ -77,6 +77,7 @@ fn main() { + } + Err(e) => { + println!("Error in CPG local_get: {e}"); ++ return Err(e); + } + } + +@@ -88,6 +89,7 @@ fn main() { + } + Err(e) => { + println!("Error in CPG membership_get: {e}"); ++ return Err(e); + } + } + +@@ -95,7 +97,7 @@ fn main() { + let set_context: u64 = 0xabcdbeefcafe; + if let Err(e) = cpg::context_set(&handle, set_context) { + println!("Error in CPG context_set: {e}"); +- return; ++ return Err(e); + } + + // NOTE This will fail on 32 bit systems because void* is not u64 +@@ -107,6 +109,7 @@ fn main() { + } + Err(e) => { + println!("Error in CPG context_get: {e}"); ++ return Err(e); + } + } + +@@ -120,6 +123,7 @@ fn main() { + } + Err(e) => { + println!("Error in CPG iter start: {e}"); ++ return Err(e); + } + } + +@@ -130,13 +134,13 @@ fn main() { + &"This is a test".to_string().into_bytes(), + ) { + println!("Error in CPG mcast_joined: {e}"); ++ return Err(e); + } + +- // Wait for events +- loop { +- if cpg::dispatch(&handle, corosync::DispatchFlags::One).is_err() { +- break; +- } ++ // Quick test of dispatch ++ if let Err(e) = cpg::dispatch(&handle, corosync::DispatchFlags::OneNonblocking) { ++ println!("Error in CPG dispatch: {e}"); ++ return Err(e); + } +- println!("ERROR: Corosync quit"); ++ Ok(()) + } +diff --git a/bindings/rust/tests/src/bin/quorum-test.rs b/bindings/rust/tests/src/bin/quorum-test.rs +index 9436b392..ae7404ec 100644 +--- a/bindings/rust/tests/src/bin/quorum-test.rs ++++ b/bindings/rust/tests/src/bin/quorum-test.rs +@@ -30,7 +30,7 @@ fn nodelist_fn( + println!(" left: {left_list:?}"); + } + +-fn main() { ++fn main() -> Result<(), corosync::CsError> { + // Initialise the model data + let md = quorum::ModelData::ModelV1(quorum::Model1Data { + flags: quorum::Model1Flags::None, +@@ -45,7 +45,7 @@ fn main() { + } + Err(e) => { + println!("Error in QUORUM init: {e}"); +- return; ++ return Err(e); + } + }; + +@@ -53,7 +53,7 @@ fn main() { + let set_context: u64 = 0xabcdbeefcafe; + if let Err(e) = quorum::context_set(&handle, set_context) { + println!("Error in QUORUM context_set: {e}"); +- return; ++ return Err(e); + } + + // NOTE This will fail on 32 bit systems because void* is not u64 +@@ -61,23 +61,24 @@ fn main() { + Ok(c) => { + if c != set_context { + println!("Error: context_get() returned {c:x}, context should be {set_context:x}"); ++ return Err(corosync::CsError::CsErrRustCompat); + } + } + Err(e) => { + println!("Error in QUORUM context_get: {e}"); ++ return Err(e); + } + } + + if let Err(e) = quorum::trackstart(&handle, corosync::TrackFlags::Changes) { + println!("Error in QUORUM trackstart: {e}"); +- return; ++ return Err(e); + } + +- // Wait for events +- loop { +- if quorum::dispatch(&handle, corosync::DispatchFlags::One).is_err() { +- break; +- } ++ // Quick test of dispatch ++ if let Err(e) = quorum::dispatch(&handle, corosync::DispatchFlags::OneNonblocking) { ++ println!("Error in QUORUM dispatch: {e}"); ++ return Err(e); + } +- println!("ERROR: Corosync quit"); ++ Ok(()) + } +diff --git a/bindings/rust/tests/src/bin/votequorum-test.rs b/bindings/rust/tests/src/bin/votequorum-test.rs +index eebc9f0d..d37100a4 100644 +--- a/bindings/rust/tests/src/bin/votequorum-test.rs ++++ b/bindings/rust/tests/src/bin/votequorum-test.rs +@@ -30,7 +30,7 @@ fn expectedvotes_fn(_handle: &votequorum::Handle, _context: u64, expected_votes: + println!("TEST expected_votes_fn called: value is {expected_votes}"); + } + +-fn main() { ++fn main() -> Result<(), corosync::CsError> { + // Initialise the model data + let cb = votequorum::Callbacks { + quorum_notification_fn: Some(quorum_fn), +@@ -45,7 +45,7 @@ fn main() { + } + Err(e) => { + println!("Error in VOTEQUORUM init: {e}"); +- return; ++ return Err(e); + } + }; + +@@ -53,6 +53,7 @@ fn main() { + let set_context: u64 = 0xabcdbeefcafe; + if let Err(e) = votequorum::context_set(&handle, set_context) { + println!("Error in VOTEQUORUM context_set: {e}"); ++ return Err(e); + } + + // NOTE This will fail on 32 bit systems because void* is not u64 +@@ -64,6 +65,7 @@ fn main() { + } + Err(e) => { + println!("Error in VOTEQUORUM context_get: {e}"); ++ return Err(e); + } + } + +@@ -71,6 +73,7 @@ fn main() { + + if let Err(e) = votequorum::qdevice_register(&handle, QDEVICE_NAME) { + println!("Error in VOTEQUORUM qdevice_register: {e}"); ++ return Err(e); + } + + match votequorum::get_info(&handle, corosync::NodeId::from(1u32)) { +@@ -91,27 +94,29 @@ fn main() { + "qdevice names do not match: s/b: \"{}\" is: \"{}\"", + QDEVICE_NAME, i.qdevice_name + ); ++ return Err(corosync::CsError::CsErrRustCompat); + } + } + Err(e) => { + println!("Error in VOTEQUORUM get_info: {e} (check nodeid 1 has been online)"); ++ return Err(e); + } + } + + if let Err(e) = votequorum::qdevice_unregister(&handle, QDEVICE_NAME) { + println!("Error in VOTEQUORUM qdevice_unregister: {e}"); ++ return Err(e); + } + + if let Err(e) = votequorum::trackstart(&handle, 99_u64, corosync::TrackFlags::Changes) { + println!("Error in VOTEQUORUM trackstart: {e}"); +- return; ++ return Err(e); + } + +- // Wait for events +- loop { +- if votequorum::dispatch(&handle, corosync::DispatchFlags::One).is_err() { +- break; +- } ++ // Quick test of dispatch ++ if let Err(e) = votequorum::dispatch(&handle, corosync::DispatchFlags::OneNonblocking) { ++ println!("Error in VOTEUORUM dispatch: {e}"); ++ return Err(e); + } +- println!("ERROR: Corosync quit"); ++ Ok(()) + } +-- +2.25.1 + diff --git a/corosync.spec b/corosync.spec index 62f6019..0ec3218 100644 --- a/corosync.spec +++ b/corosync.spec @@ -18,7 +18,7 @@ Name: corosync Summary: The Corosync Cluster Engine and Application Programming Interfaces Version: 3.1.8 -Release: 6 +Release: 7 License: BSD-3-Clause URL: http://corosync.github.io/corosync/ Source0: http://build.clusterlabs.org/corosync/releases/%{name}-%{version}%{?gittarver}.tar.gz @@ -27,7 +27,8 @@ Patch1: Report-crypto-errors-back-to-cfg-reload.patch Patch2: Fix-building-of-rust-for-release.patch Patch3: totem-Fix-reference-links.patch Patch4: backport-Move-corosync-notifyd-policy-file-into-datadir.patch - +Patch5: backport-rust-Improve-Rust-bindings.patch +Patch6: backport-rust-tests-return-errors-and-don-t-hang.patch # Runtime bits # The automatic dependency overridden in favor of explicit version lock Requires: corosynclib = %{version}-%{release} @@ -293,6 +294,10 @@ network splits) %endif %changelog +* Thu Sep 19 2024 bizhiyuan - 3.1.8-7 +- rust: remove rust bindings +- rust: tests return errors and don't hang + * Thu Jun 06 2024 zouzhimin - 3.1.8-6 - Move corosync-notifyd policy file into $(datadir)/dbus-1/system.d and add yaml file -- Gitee