cfg.rs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374
  1. // libcfg 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. // For the code generated by bindgen
  9. use crate::sys::cfg as ffi;
  10. use std::collections::HashMap;
  11. use std::ffi::CString;
  12. use std::os::raw::{c_int, c_void};
  13. use std::sync::Mutex;
  14. use crate::string_from_bytes;
  15. use crate::{CsError, DispatchFlags, NodeId, Result};
  16. // Used to convert a CFG handle into one of ours
  17. lazy_static! {
  18. static ref HANDLE_HASH: Mutex<HashMap<u64, Handle>> = Mutex::new(HashMap::new());
  19. }
  20. /// Callback from [track_start]. Will be called if another process
  21. /// requests to shut down corosync. [reply_to_shutdown] should be called
  22. /// with a [ShutdownReply] of either Yes or No.
  23. #[derive(Copy, Clone)]
  24. pub struct Callbacks {
  25. pub corosync_cfg_shutdown_callback_fn: Option<fn(handle: &Handle, flags: u32)>,
  26. }
  27. /// A handle into the cfg library. returned from [initialize] and needed for all other calls
  28. pub struct Handle {
  29. cfg_handle: u64,
  30. callbacks: Callbacks,
  31. clone: bool,
  32. }
  33. impl Clone for Handle {
  34. fn clone(&self) -> Handle {
  35. Handle {
  36. cfg_handle: self.cfg_handle,
  37. callbacks: self.callbacks,
  38. clone: true,
  39. }
  40. }
  41. }
  42. impl Drop for Handle {
  43. fn drop(self: &mut Handle) {
  44. if !self.clone {
  45. let _e = finalize(self);
  46. }
  47. }
  48. }
  49. // Clones count as equivalent
  50. impl PartialEq for Handle {
  51. fn eq(&self, other: &Handle) -> bool {
  52. self.cfg_handle == other.cfg_handle
  53. }
  54. }
  55. /// Flags for [try_shutdown]
  56. pub enum ShutdownFlags {
  57. /// Request shutdown (other daemons will be consulted)
  58. Request,
  59. /// Tells other daemons but ignore their opinions
  60. Regardless,
  61. /// Go down straight away (but still tell other nodes)
  62. Immediate,
  63. }
  64. /// Responses for [reply_to_shutdown]
  65. pub enum ShutdownReply {
  66. Yes = 1,
  67. No = 0,
  68. }
  69. /// Trackflags for [track_start]. None currently supported
  70. pub enum TrackFlags {
  71. None,
  72. }
  73. /// Version of the [NodeStatus] structure returned from [node_status_get]
  74. #[derive(Debug, Copy, Clone)]
  75. pub enum NodeStatusVersion {
  76. V1,
  77. }
  78. /// Status of a link inside [NodeStatus] struct
  79. #[derive(Debug)]
  80. pub struct LinkStatus {
  81. pub enabled: bool,
  82. pub connected: bool,
  83. pub dynconnected: bool,
  84. pub mtu: u32,
  85. pub src_ipaddr: String,
  86. pub dst_ipaddr: String,
  87. }
  88. /// Structure returned from [node_status_get], shows all the details of a node
  89. /// that is known to corosync, including all configured links
  90. #[derive(Debug)]
  91. pub struct NodeStatus {
  92. pub version: NodeStatusVersion,
  93. pub nodeid: NodeId,
  94. pub reachable: bool,
  95. pub remote: bool,
  96. pub external: bool,
  97. pub onwire_min: u8,
  98. pub onwire_max: u8,
  99. pub onwire_ver: u8,
  100. pub link_status: Vec<LinkStatus>,
  101. }
  102. extern "C" fn rust_shutdown_notification_fn(handle: ffi::corosync_cfg_handle_t, flags: u32) {
  103. if let Some(h) = HANDLE_HASH.lock().unwrap().get(&handle) {
  104. if let Some(cb) = h.callbacks.corosync_cfg_shutdown_callback_fn {
  105. (cb)(h, flags);
  106. }
  107. }
  108. }
  109. /// Initialize a connection to the cfg library. You must call this before doing anything
  110. /// else and use the passed back [Handle].
  111. /// Remember to free the handle using [finalize] when finished.
  112. pub fn initialize(callbacks: &Callbacks) -> Result<Handle> {
  113. let mut handle: ffi::corosync_cfg_handle_t = 0;
  114. let c_callbacks = ffi::corosync_cfg_callbacks_t {
  115. corosync_cfg_shutdown_callback: Some(rust_shutdown_notification_fn),
  116. };
  117. unsafe {
  118. let res = ffi::corosync_cfg_initialize(&mut handle, &c_callbacks);
  119. if res == ffi::CS_OK {
  120. let rhandle = Handle {
  121. cfg_handle: handle,
  122. callbacks: *callbacks,
  123. clone: false,
  124. };
  125. HANDLE_HASH.lock().unwrap().insert(handle, rhandle.clone());
  126. Ok(rhandle)
  127. } else {
  128. Err(CsError::from_c(res))
  129. }
  130. }
  131. }
  132. /// Finish with a connection to corosync, after calling this the [Handle] is invalid
  133. pub fn finalize(handle: &Handle) -> Result<()> {
  134. let res = unsafe { ffi::corosync_cfg_finalize(handle.cfg_handle) };
  135. if res == ffi::CS_OK {
  136. HANDLE_HASH.lock().unwrap().remove(&handle.cfg_handle);
  137. Ok(())
  138. } else {
  139. Err(CsError::from_c(res))
  140. }
  141. }
  142. // not sure if an fd is the right thing to return here, but it will do for now.
  143. /// Returns a file descriptor to use for poll/select on the CFG handle
  144. pub fn fd_get(handle: &Handle) -> Result<i32> {
  145. let c_fd: *mut c_int = &mut 0 as *mut _ as *mut c_int;
  146. let res = unsafe { ffi::corosync_cfg_fd_get(handle.cfg_handle, c_fd) };
  147. if res == ffi::CS_OK {
  148. Ok(c_fd as i32)
  149. } else {
  150. Err(CsError::from_c(res))
  151. }
  152. }
  153. /// Get the local [NodeId]
  154. pub fn local_get(handle: &Handle) -> Result<NodeId> {
  155. let mut nodeid: u32 = 0;
  156. let res = unsafe { ffi::corosync_cfg_local_get(handle.cfg_handle, &mut nodeid) };
  157. if res == ffi::CS_OK {
  158. Ok(NodeId::from(nodeid))
  159. } else {
  160. Err(CsError::from_c(res))
  161. }
  162. }
  163. /// Reload the cluster configuration on all nodes
  164. pub fn reload_cnfig(handle: &Handle) -> Result<()> {
  165. let res = unsafe { ffi::corosync_cfg_reload_config(handle.cfg_handle) };
  166. if res == ffi::CS_OK {
  167. Ok(())
  168. } else {
  169. Err(CsError::from_c(res))
  170. }
  171. }
  172. /// Re-open the cluster log files, on this node only
  173. pub fn reopen_log_files(handle: &Handle) -> Result<()> {
  174. let res = unsafe { ffi::corosync_cfg_reopen_log_files(handle.cfg_handle) };
  175. if res == ffi::CS_OK {
  176. Ok(())
  177. } else {
  178. Err(CsError::from_c(res))
  179. }
  180. }
  181. /// Tell another cluster node to shutdown. reason is a string that
  182. /// will be written to the system log files.
  183. pub fn kill_node(handle: &Handle, nodeid: NodeId, reason: &str) -> Result<()> {
  184. let c_string = {
  185. match CString::new(reason) {
  186. Ok(cs) => cs,
  187. Err(_) => return Err(CsError::CsErrInvalidParam),
  188. }
  189. };
  190. let res = unsafe {
  191. ffi::corosync_cfg_kill_node(handle.cfg_handle, u32::from(nodeid), c_string.as_ptr())
  192. };
  193. if res == ffi::CS_OK {
  194. Ok(())
  195. } else {
  196. Err(CsError::from_c(res))
  197. }
  198. }
  199. /// Ask this cluster node to shutdown. If [ShutdownFlags] is set to Request then
  200. ///it may be refused by other applications
  201. /// that have registered for shutdown callbacks.
  202. pub fn try_shutdown(handle: &Handle, flags: ShutdownFlags) -> Result<()> {
  203. let c_flags = match flags {
  204. ShutdownFlags::Request => 0,
  205. ShutdownFlags::Regardless => 1,
  206. ShutdownFlags::Immediate => 2,
  207. };
  208. let res = unsafe { ffi::corosync_cfg_try_shutdown(handle.cfg_handle, c_flags) };
  209. if res == ffi::CS_OK {
  210. Ok(())
  211. } else {
  212. Err(CsError::from_c(res))
  213. }
  214. }
  215. /// Reply to a shutdown request with Yes or No [ShutdownReply]
  216. pub fn reply_to_shutdown(handle: &Handle, flags: ShutdownReply) -> Result<()> {
  217. let c_flags = match flags {
  218. ShutdownReply::No => 0,
  219. ShutdownReply::Yes => 1,
  220. };
  221. let res = unsafe { ffi::corosync_cfg_replyto_shutdown(handle.cfg_handle, c_flags) };
  222. if res == ffi::CS_OK {
  223. Ok(())
  224. } else {
  225. Err(CsError::from_c(res))
  226. }
  227. }
  228. /// Call any/all active CFG callbacks for this [Handle] see [DispatchFlags] for details
  229. pub fn dispatch(handle: &Handle, flags: DispatchFlags) -> Result<()> {
  230. let res = unsafe { ffi::corosync_cfg_dispatch(handle.cfg_handle, flags as u32) };
  231. if res == ffi::CS_OK {
  232. Ok(())
  233. } else {
  234. Err(CsError::from_c(res))
  235. }
  236. }
  237. // Quick & dirty u8 to boolean
  238. fn u8_to_bool(val: u8) -> bool {
  239. val != 0
  240. }
  241. const CFG_MAX_LINKS: usize = 8;
  242. const CFG_MAX_HOST_LEN: usize = 256;
  243. fn unpack_nodestatus(c_nodestatus: ffi::corosync_cfg_node_status_v1) -> Result<NodeStatus> {
  244. let mut ns = NodeStatus {
  245. version: NodeStatusVersion::V1,
  246. nodeid: NodeId::from(c_nodestatus.nodeid),
  247. reachable: u8_to_bool(c_nodestatus.reachable),
  248. remote: u8_to_bool(c_nodestatus.remote),
  249. external: u8_to_bool(c_nodestatus.external),
  250. onwire_min: c_nodestatus.onwire_min,
  251. onwire_max: c_nodestatus.onwire_max,
  252. onwire_ver: c_nodestatus.onwire_min,
  253. link_status: Vec::<LinkStatus>::new(),
  254. };
  255. for i in 0..CFG_MAX_LINKS {
  256. let ls = LinkStatus {
  257. enabled: u8_to_bool(c_nodestatus.link_status[i].enabled),
  258. connected: u8_to_bool(c_nodestatus.link_status[i].connected),
  259. dynconnected: u8_to_bool(c_nodestatus.link_status[i].dynconnected),
  260. mtu: c_nodestatus.link_status[i].mtu,
  261. src_ipaddr: string_from_bytes(
  262. &c_nodestatus.link_status[i].src_ipaddr[0],
  263. CFG_MAX_HOST_LEN,
  264. )?,
  265. dst_ipaddr: string_from_bytes(
  266. &c_nodestatus.link_status[i].dst_ipaddr[0],
  267. CFG_MAX_HOST_LEN,
  268. )?,
  269. };
  270. ns.link_status.push(ls);
  271. }
  272. Ok(ns)
  273. }
  274. // Constructor for link status to make c_ndostatus initialization tidier.
  275. fn new_ls() -> ffi::corosync_knet_link_status_v1 {
  276. ffi::corosync_knet_link_status_v1 {
  277. enabled: 0,
  278. connected: 0,
  279. dynconnected: 0,
  280. mtu: 0,
  281. src_ipaddr: [0; 256],
  282. dst_ipaddr: [0; 256],
  283. }
  284. }
  285. /// Get the extended status of a node in the cluster (including active links) from its [NodeId].
  286. /// Returns a filled in [NodeStatus] struct
  287. pub fn node_status_get(
  288. handle: &Handle,
  289. nodeid: NodeId,
  290. _version: NodeStatusVersion,
  291. ) -> Result<NodeStatus> {
  292. // Currently only supports V1 struct
  293. unsafe {
  294. // We need to initialize this even though it's all going to be overwritten.
  295. let mut c_nodestatus = ffi::corosync_cfg_node_status_v1 {
  296. version: 1,
  297. nodeid: 0,
  298. reachable: 0,
  299. remote: 0,
  300. external: 0,
  301. onwire_min: 0,
  302. onwire_max: 0,
  303. onwire_ver: 0,
  304. link_status: [new_ls(); 8],
  305. };
  306. let res = ffi::corosync_cfg_node_status_get(
  307. handle.cfg_handle,
  308. u32::from(nodeid),
  309. 1,
  310. &mut c_nodestatus as *mut _ as *mut c_void,
  311. );
  312. if res == ffi::CS_OK {
  313. unpack_nodestatus(c_nodestatus)
  314. } else {
  315. Err(CsError::from_c(res))
  316. }
  317. }
  318. }
  319. /// Start tracking for shutdown notifications
  320. pub fn track_start(handle: &Handle, _flags: TrackFlags) -> Result<()> {
  321. let res = unsafe { ffi::corosync_cfg_trackstart(handle.cfg_handle, 0) };
  322. if res == ffi::CS_OK {
  323. Ok(())
  324. } else {
  325. Err(CsError::from_c(res))
  326. }
  327. }
  328. /// Stop tracking for shutdown notifications
  329. pub fn track_stop(handle: &Handle) -> Result<()> {
  330. let res = unsafe { ffi::corosync_cfg_trackstop(handle.cfg_handle) };
  331. if res == ffi::CS_OK {
  332. Ok(())
  333. } else {
  334. Err(CsError::from_c(res))
  335. }
  336. }