1use crate::sys::cfg as ffi;
11
12use std::collections::HashMap;
13use std::ffi::CString;
14use std::os::raw::{c_int, c_void};
15use std::sync::Mutex;
16
17use crate::string_from_bytes;
18use crate::{CsError, DispatchFlags, NodeId, Result};
19
20lazy_static! {
22 static ref HANDLE_HASH: Mutex<HashMap<u64, Handle>> = Mutex::new(HashMap::new());
23}
24
25#[derive(Copy, Clone)]
29pub struct Callbacks {
30 pub corosync_cfg_shutdown_callback_fn: Option<fn(handle: &Handle, flags: u32)>,
31}
32
33pub struct Handle {
35 cfg_handle: u64,
36 callbacks: Callbacks,
37 clone: bool,
38}
39
40impl Clone for Handle {
41 fn clone(&self) -> Handle {
42 Handle {
43 cfg_handle: self.cfg_handle,
44 callbacks: self.callbacks,
45 clone: true,
46 }
47 }
48}
49
50impl Drop for Handle {
51 fn drop(self: &mut Handle) {
52 if !self.clone {
53 let _e = finalize(self);
54 }
55 }
56}
57
58impl PartialEq for Handle {
60 fn eq(&self, other: &Handle) -> bool {
61 self.cfg_handle == other.cfg_handle
62 }
63}
64
65pub enum ShutdownFlags {
67 Request,
69 Regardless,
71 Immediate,
73}
74
75pub enum ShutdownReply {
77 Yes = 1,
78 No = 0,
79}
80
81pub enum TrackFlags {
83 None,
84}
85
86#[derive(Debug, Copy, Clone)]
88pub enum NodeStatusVersion {
89 V1,
90}
91
92#[derive(Debug)]
94pub struct LinkStatus {
95 pub enabled: bool,
96 pub connected: bool,
97 pub dynconnected: bool,
98 pub mtu: u32,
99 pub src_ipaddr: String,
100 pub dst_ipaddr: String,
101}
102
103#[derive(Debug)]
106pub struct NodeStatus {
107 pub version: NodeStatusVersion,
108 pub nodeid: NodeId,
109 pub reachable: bool,
110 pub remote: bool,
111 pub external: bool,
112 pub onwire_min: u8,
113 pub onwire_max: u8,
114 pub onwire_ver: u8,
115 pub link_status: Vec<LinkStatus>,
116}
117
118extern "C" fn rust_shutdown_notification_fn(handle: ffi::corosync_cfg_handle_t, flags: u32) {
119 if let Some(h) = HANDLE_HASH.lock().unwrap().get(&handle) {
120 if let Some(cb) = h.callbacks.corosync_cfg_shutdown_callback_fn {
121 (cb)(h, flags);
122 }
123 }
124}
125
126pub fn initialize(callbacks: &Callbacks) -> Result<Handle> {
130 let mut handle: ffi::corosync_cfg_handle_t = 0;
131
132 let c_callbacks = ffi::corosync_cfg_callbacks_t {
133 corosync_cfg_shutdown_callback: Some(rust_shutdown_notification_fn),
134 };
135
136 unsafe {
137 let res = ffi::corosync_cfg_initialize(&mut handle, &c_callbacks);
138 if res == ffi::CS_OK {
139 let rhandle = Handle {
140 cfg_handle: handle,
141 callbacks: *callbacks,
142 clone: false,
143 };
144 HANDLE_HASH.lock().unwrap().insert(handle, rhandle.clone());
145 Ok(rhandle)
146 } else {
147 Err(CsError::from_c(res))
148 }
149 }
150}
151
152pub fn finalize(handle: &Handle) -> Result<()> {
154 let res = unsafe { ffi::corosync_cfg_finalize(handle.cfg_handle) };
155 if res == ffi::CS_OK {
156 HANDLE_HASH.lock().unwrap().remove(&handle.cfg_handle);
157 Ok(())
158 } else {
159 Err(CsError::from_c(res))
160 }
161}
162
163pub fn fd_get(handle: &Handle) -> Result<i32> {
166 let c_fd: *mut c_int = &mut 0 as *mut _ as *mut c_int;
167 let res = unsafe { ffi::corosync_cfg_fd_get(handle.cfg_handle, c_fd) };
168 if res == ffi::CS_OK {
169 Ok(unsafe { *c_fd })
170 } else {
171 Err(CsError::from_c(res))
172 }
173}
174
175pub fn local_get(handle: &Handle) -> Result<NodeId> {
177 let mut nodeid: u32 = 0;
178 let res = unsafe { ffi::corosync_cfg_local_get(handle.cfg_handle, &mut nodeid) };
179 if res == ffi::CS_OK {
180 Ok(NodeId::from(nodeid))
181 } else {
182 Err(CsError::from_c(res))
183 }
184}
185
186pub fn reload_cnfig(handle: &Handle) -> Result<()> {
188 let res = unsafe { ffi::corosync_cfg_reload_config(handle.cfg_handle) };
189 if res == ffi::CS_OK {
190 Ok(())
191 } else {
192 Err(CsError::from_c(res))
193 }
194}
195
196pub fn reopen_log_files(handle: &Handle) -> Result<()> {
198 let res = unsafe { ffi::corosync_cfg_reopen_log_files(handle.cfg_handle) };
199 if res == ffi::CS_OK {
200 Ok(())
201 } else {
202 Err(CsError::from_c(res))
203 }
204}
205
206pub fn kill_node(handle: &Handle, nodeid: NodeId, reason: &str) -> Result<()> {
209 let c_string = {
210 match CString::new(reason) {
211 Ok(cs) => cs,
212 Err(_) => return Err(CsError::CsErrInvalidParam),
213 }
214 };
215
216 let res = unsafe {
217 ffi::corosync_cfg_kill_node(handle.cfg_handle, u32::from(nodeid), c_string.as_ptr())
218 };
219 if res == ffi::CS_OK {
220 Ok(())
221 } else {
222 Err(CsError::from_c(res))
223 }
224}
225
226pub fn try_shutdown(handle: &Handle, flags: ShutdownFlags) -> Result<()> {
230 let c_flags = match flags {
231 ShutdownFlags::Request => 0,
232 ShutdownFlags::Regardless => 1,
233 ShutdownFlags::Immediate => 2,
234 };
235 let res = unsafe { ffi::corosync_cfg_try_shutdown(handle.cfg_handle, c_flags) };
236 if res == ffi::CS_OK {
237 Ok(())
238 } else {
239 Err(CsError::from_c(res))
240 }
241}
242
243pub fn reply_to_shutdown(handle: &Handle, flags: ShutdownReply) -> Result<()> {
245 let c_flags = match flags {
246 ShutdownReply::No => 0,
247 ShutdownReply::Yes => 1,
248 };
249 let res = unsafe { ffi::corosync_cfg_replyto_shutdown(handle.cfg_handle, c_flags) };
250 if res == ffi::CS_OK {
251 Ok(())
252 } else {
253 Err(CsError::from_c(res))
254 }
255}
256
257pub fn dispatch(handle: &Handle, flags: DispatchFlags) -> Result<()> {
259 let res = unsafe { ffi::corosync_cfg_dispatch(handle.cfg_handle, flags as u32) };
260 if res == ffi::CS_OK {
261 Ok(())
262 } else {
263 Err(CsError::from_c(res))
264 }
265}
266
267fn u8_to_bool(val: u8) -> bool {
269 val != 0
270}
271
272const CFG_MAX_LINKS: usize = 8;
273const CFG_MAX_HOST_LEN: usize = 256;
274fn unpack_nodestatus(c_nodestatus: ffi::corosync_cfg_node_status_v1) -> Result<NodeStatus> {
275 let mut ns = NodeStatus {
276 version: NodeStatusVersion::V1,
277 nodeid: NodeId::from(c_nodestatus.nodeid),
278 reachable: u8_to_bool(c_nodestatus.reachable),
279 remote: u8_to_bool(c_nodestatus.remote),
280 external: u8_to_bool(c_nodestatus.external),
281 onwire_min: c_nodestatus.onwire_min,
282 onwire_max: c_nodestatus.onwire_max,
283 onwire_ver: c_nodestatus.onwire_min,
284 link_status: Vec::<LinkStatus>::new(),
285 };
286 for i in 0..CFG_MAX_LINKS {
287 let ls = LinkStatus {
288 enabled: u8_to_bool(c_nodestatus.link_status[i].enabled),
289 connected: u8_to_bool(c_nodestatus.link_status[i].connected),
290 dynconnected: u8_to_bool(c_nodestatus.link_status[i].dynconnected),
291 mtu: c_nodestatus.link_status[i].mtu,
292 src_ipaddr: string_from_bytes(
293 &c_nodestatus.link_status[i].src_ipaddr[0],
294 CFG_MAX_HOST_LEN,
295 )?,
296 dst_ipaddr: string_from_bytes(
297 &c_nodestatus.link_status[i].dst_ipaddr[0],
298 CFG_MAX_HOST_LEN,
299 )?,
300 };
301 ns.link_status.push(ls);
302 }
303
304 Ok(ns)
305}
306
307fn new_ls() -> ffi::corosync_knet_link_status_v1 {
309 ffi::corosync_knet_link_status_v1 {
310 enabled: 0,
311 connected: 0,
312 dynconnected: 0,
313 mtu: 0,
314 src_ipaddr: [0; 256],
315 dst_ipaddr: [0; 256],
316 }
317}
318
319pub fn node_status_get(
322 handle: &Handle,
323 nodeid: NodeId,
324 _version: NodeStatusVersion,
325) -> Result<NodeStatus> {
326 unsafe {
328 let mut c_nodestatus = ffi::corosync_cfg_node_status_v1 {
330 version: 1,
331 nodeid: 0,
332 reachable: 0,
333 remote: 0,
334 external: 0,
335 onwire_min: 0,
336 onwire_max: 0,
337 onwire_ver: 0,
338 link_status: [new_ls(); 8],
339 };
340
341 let res = ffi::corosync_cfg_node_status_get(
342 handle.cfg_handle,
343 u32::from(nodeid),
344 1,
345 &mut c_nodestatus as *mut _ as *mut c_void,
346 );
347
348 if res == ffi::CS_OK {
349 unpack_nodestatus(c_nodestatus)
350 } else {
351 Err(CsError::from_c(res))
352 }
353 }
354}
355
356pub fn track_start(handle: &Handle, _flags: TrackFlags) -> Result<()> {
358 let res = unsafe { ffi::corosync_cfg_trackstart(handle.cfg_handle, 0) };
359 if res == ffi::CS_OK {
360 Ok(())
361 } else {
362 Err(CsError::from_c(res))
363 }
364}
365
366pub fn track_stop(handle: &Handle) -> Result<()> {
368 let res = unsafe { ffi::corosync_cfg_trackstop(handle.cfg_handle) };
369 if res == ffi::CS_OK {
370 Ok(())
371 } else {
372 Err(CsError::from_c(res))
373 }
374}