| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374 |
- // libcfg interface for Rust
- // Copyright (c) 2021 Red Hat, Inc.
- //
- // All rights reserved.
- //
- // Author: Christine Caulfield (ccaulfi@redhat.com)
- //
- // For the code generated by bindgen
- use crate::sys::cfg as ffi;
- use std::collections::HashMap;
- use std::ffi::CString;
- use std::os::raw::{c_int, c_void};
- use std::sync::Mutex;
- use crate::string_from_bytes;
- use crate::{CsError, DispatchFlags, NodeId, Result};
- // Used to convert a CFG handle into one of ours
- lazy_static! {
- static ref HANDLE_HASH: Mutex<HashMap<u64, Handle>> = Mutex::new(HashMap::new());
- }
- /// Callback from [track_start]. Will be called if another process
- /// requests to shut down corosync. [reply_to_shutdown] should be called
- /// with a [ShutdownReply] of either Yes or No.
- #[derive(Copy, Clone)]
- pub struct Callbacks {
- pub corosync_cfg_shutdown_callback_fn: Option<fn(handle: &Handle, flags: u32)>,
- }
- /// A handle into the cfg library. returned from [initialize] and needed for all other calls
- 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]
- pub enum ShutdownFlags {
- /// Request shutdown (other daemons will be consulted)
- Request,
- /// Tells other daemons but ignore their opinions
- Regardless,
- /// Go down straight away (but still tell other nodes)
- Immediate,
- }
- /// Responses for [reply_to_shutdown]
- pub enum ShutdownReply {
- Yes = 1,
- No = 0,
- }
- /// Trackflags for [track_start]. None currently supported
- pub enum TrackFlags {
- None,
- }
- /// Version of the [NodeStatus] structure returned from [node_status_get]
- #[derive(Debug, Copy, Clone)]
- pub enum NodeStatusVersion {
- V1,
- }
- /// Status of a link inside [NodeStatus] struct
- #[derive(Debug)]
- pub struct LinkStatus {
- pub enabled: bool,
- pub connected: bool,
- pub dynconnected: bool,
- pub mtu: u32,
- pub src_ipaddr: String,
- pub dst_ipaddr: String,
- }
- /// Structure returned from [node_status_get], shows all the details of a node
- /// that is known to corosync, including all configured links
- #[derive(Debug)]
- pub struct NodeStatus {
- pub version: NodeStatusVersion,
- pub nodeid: NodeId,
- pub reachable: bool,
- pub remote: bool,
- pub external: bool,
- pub onwire_min: u8,
- pub onwire_max: u8,
- pub onwire_ver: u8,
- pub link_status: Vec<LinkStatus>,
- }
- extern "C" fn rust_shutdown_notification_fn(handle: ffi::corosync_cfg_handle_t, flags: u32) {
- if let Some(h) = HANDLE_HASH.lock().unwrap().get(&handle) {
- if let Some(cb) = h.callbacks.corosync_cfg_shutdown_callback_fn {
- (cb)(h, flags);
- }
- }
- }
- /// Initialize a connection to the cfg library. You must call this before doing anything
- /// else and use the passed back [Handle].
- /// Remember to free the handle using [finalize] when finished.
- pub fn initialize(callbacks: &Callbacks) -> Result<Handle> {
- let mut handle: ffi::corosync_cfg_handle_t = 0;
- let c_callbacks = ffi::corosync_cfg_callbacks_t {
- corosync_cfg_shutdown_callback: Some(rust_shutdown_notification_fn),
- };
- unsafe {
- let res = ffi::corosync_cfg_initialize(&mut handle, &c_callbacks);
- if res == ffi::CS_OK {
- let rhandle = Handle {
- cfg_handle: handle,
- callbacks: *callbacks,
- clone: false,
- };
- HANDLE_HASH.lock().unwrap().insert(handle, rhandle.clone());
- Ok(rhandle)
- } else {
- Err(CsError::from_c(res))
- }
- }
- }
- /// Finish with a connection to corosync, after calling this the [Handle] is invalid
- 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);
- Ok(())
- } else {
- Err(CsError::from_c(res))
- }
- }
- // 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<i32> {
- 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 {
- Ok(c_fd as i32)
- } else {
- Err(CsError::from_c(res))
- }
- }
- /// Get the local [NodeId]
- pub fn local_get(handle: &Handle) -> Result<NodeId> {
- let mut nodeid: u32 = 0;
- let res = unsafe { ffi::corosync_cfg_local_get(handle.cfg_handle, &mut nodeid) };
- if res == ffi::CS_OK {
- Ok(NodeId::from(nodeid))
- } else {
- Err(CsError::from_c(res))
- }
- }
- /// Reload the cluster configuration on all nodes
- pub fn reload_cnfig(handle: &Handle) -> Result<()> {
- let res = unsafe { ffi::corosync_cfg_reload_config(handle.cfg_handle) };
- if res == ffi::CS_OK {
- Ok(())
- } else {
- Err(CsError::from_c(res))
- }
- }
- /// Re-open the cluster log files, on this node only
- 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(())
- } else {
- Err(CsError::from_c(res))
- }
- }
- /// 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<()> {
- let c_string = {
- match CString::new(reason) {
- Ok(cs) => cs,
- Err(_) => return Err(CsError::CsErrInvalidParam),
- }
- };
- let res = unsafe {
- ffi::corosync_cfg_kill_node(handle.cfg_handle, u32::from(nodeid), c_string.as_ptr())
- };
- if res == ffi::CS_OK {
- Ok(())
- } else {
- Err(CsError::from_c(res))
- }
- }
- /// 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<()> {
- let c_flags = match flags {
- ShutdownFlags::Request => 0,
- ShutdownFlags::Regardless => 1,
- ShutdownFlags::Immediate => 2,
- };
- let res = unsafe { ffi::corosync_cfg_try_shutdown(handle.cfg_handle, c_flags) };
- if res == ffi::CS_OK {
- Ok(())
- } else {
- Err(CsError::from_c(res))
- }
- }
- /// Reply to a shutdown request with Yes or No [ShutdownReply]
- pub fn reply_to_shutdown(handle: &Handle, flags: ShutdownReply) -> Result<()> {
- let c_flags = match flags {
- ShutdownReply::No => 0,
- ShutdownReply::Yes => 1,
- };
- let res = unsafe { ffi::corosync_cfg_replyto_shutdown(handle.cfg_handle, c_flags) };
- if res == ffi::CS_OK {
- Ok(())
- } else {
- Err(CsError::from_c(res))
- }
- }
- /// Call any/all active CFG callbacks for this [Handle] see [DispatchFlags] for details
- 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(())
- } else {
- Err(CsError::from_c(res))
- }
- }
- // Quick & dirty u8 to boolean
- fn u8_to_bool(val: u8) -> bool {
- val != 0
- }
- const CFG_MAX_LINKS: usize = 8;
- const CFG_MAX_HOST_LEN: usize = 256;
- fn unpack_nodestatus(c_nodestatus: ffi::corosync_cfg_node_status_v1) -> Result<NodeStatus> {
- let mut ns = NodeStatus {
- version: NodeStatusVersion::V1,
- nodeid: NodeId::from(c_nodestatus.nodeid),
- reachable: u8_to_bool(c_nodestatus.reachable),
- remote: u8_to_bool(c_nodestatus.remote),
- external: u8_to_bool(c_nodestatus.external),
- onwire_min: c_nodestatus.onwire_min,
- onwire_max: c_nodestatus.onwire_max,
- onwire_ver: c_nodestatus.onwire_min,
- link_status: Vec::<LinkStatus>::new(),
- };
- for i in 0..CFG_MAX_LINKS {
- let ls = LinkStatus {
- enabled: u8_to_bool(c_nodestatus.link_status[i].enabled),
- connected: u8_to_bool(c_nodestatus.link_status[i].connected),
- dynconnected: u8_to_bool(c_nodestatus.link_status[i].dynconnected),
- mtu: c_nodestatus.link_status[i].mtu,
- src_ipaddr: string_from_bytes(
- &c_nodestatus.link_status[i].src_ipaddr[0],
- CFG_MAX_HOST_LEN,
- )?,
- dst_ipaddr: string_from_bytes(
- &c_nodestatus.link_status[i].dst_ipaddr[0],
- CFG_MAX_HOST_LEN,
- )?,
- };
- ns.link_status.push(ls);
- }
- Ok(ns)
- }
- // Constructor for link status to make c_ndostatus initialization tidier.
- fn new_ls() -> ffi::corosync_knet_link_status_v1 {
- ffi::corosync_knet_link_status_v1 {
- enabled: 0,
- connected: 0,
- dynconnected: 0,
- mtu: 0,
- src_ipaddr: [0; 256],
- dst_ipaddr: [0; 256],
- }
- }
- /// 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,
- nodeid: NodeId,
- _version: NodeStatusVersion,
- ) -> Result<NodeStatus> {
- // Currently only supports V1 struct
- unsafe {
- // We need to initialize this even though it's all going to be overwritten.
- let mut c_nodestatus = ffi::corosync_cfg_node_status_v1 {
- version: 1,
- nodeid: 0,
- reachable: 0,
- remote: 0,
- external: 0,
- onwire_min: 0,
- onwire_max: 0,
- onwire_ver: 0,
- link_status: [new_ls(); 8],
- };
- let res = ffi::corosync_cfg_node_status_get(
- handle.cfg_handle,
- u32::from(nodeid),
- 1,
- &mut c_nodestatus as *mut _ as *mut c_void,
- );
- if res == ffi::CS_OK {
- unpack_nodestatus(c_nodestatus)
- } else {
- Err(CsError::from_c(res))
- }
- }
- }
- /// Start tracking for shutdown notifications
- 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(())
- } else {
- Err(CsError::from_c(res))
- }
- }
- /// Stop tracking for shutdown notifications
- pub fn track_stop(handle: &Handle) -> Result<()> {
- let res = unsafe { ffi::corosync_cfg_trackstop(handle.cfg_handle) };
- if res == ffi::CS_OK {
- Ok(())
- } else {
- Err(CsError::from_c(res))
- }
- }
|