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
33#[derive(Copy, Clone)]
35pub struct Handle {
36 cfg_handle: u64,
37 callbacks: Callbacks,
38}
39
40pub enum ShutdownFlags {
42 Request,
44 Regardless,
46 Immediate,
48}
49
50pub enum ShutdownReply {
52 Yes = 1,
53 No = 0,
54}
55
56pub enum TrackFlags {
58 None,
59}
60
61#[derive(Debug, Copy, Clone)]
63pub enum NodeStatusVersion {
64 V1,
65}
66
67#[derive(Debug)]
69pub struct LinkStatus {
70 pub enabled: bool,
71 pub connected: bool,
72 pub dynconnected: bool,
73 pub mtu: u32,
74 pub src_ipaddr: String,
75 pub dst_ipaddr: String,
76}
77
78#[derive(Debug)]
81pub struct NodeStatus {
82 pub version: NodeStatusVersion,
83 pub nodeid: NodeId,
84 pub reachable: bool,
85 pub remote: bool,
86 pub external: bool,
87 pub onwire_min: u8,
88 pub onwire_max: u8,
89 pub onwire_ver: u8,
90 pub link_status: Vec<LinkStatus>,
91}
92
93extern "C" fn rust_shutdown_notification_fn(handle: ffi::corosync_cfg_handle_t, flags: u32) {
94 if let Some(h) = HANDLE_HASH.lock().unwrap().get(&handle) {
95 if let Some(cb) = h.callbacks.corosync_cfg_shutdown_callback_fn {
96 (cb)(h, flags);
97 }
98 }
99}
100
101pub fn initialize(callbacks: &Callbacks) -> Result<Handle> {
105 let mut handle: ffi::corosync_cfg_handle_t = 0;
106
107 let c_callbacks = ffi::corosync_cfg_callbacks_t {
108 corosync_cfg_shutdown_callback: Some(rust_shutdown_notification_fn),
109 };
110
111 unsafe {
112 let res = ffi::corosync_cfg_initialize(&mut handle, &c_callbacks);
113 if res == ffi::CS_OK {
114 let rhandle = Handle {
115 cfg_handle: handle,
116 callbacks: *callbacks,
117 };
118 HANDLE_HASH.lock().unwrap().insert(handle, rhandle);
119 Ok(rhandle)
120 } else {
121 Err(CsError::from_c(res))
122 }
123 }
124}
125
126pub fn finalize(handle: Handle) -> Result<()> {
128 let res = unsafe { ffi::corosync_cfg_finalize(handle.cfg_handle) };
129 if res == ffi::CS_OK {
130 HANDLE_HASH.lock().unwrap().remove(&handle.cfg_handle);
131 Ok(())
132 } else {
133 Err(CsError::from_c(res))
134 }
135}
136
137pub fn fd_get(handle: Handle) -> Result<i32> {
140 let c_fd: *mut c_int = &mut 0 as *mut _ as *mut c_int;
141 let res = unsafe { ffi::corosync_cfg_fd_get(handle.cfg_handle, c_fd) };
142 if res == ffi::CS_OK {
143 Ok(c_fd as i32)
144 } else {
145 Err(CsError::from_c(res))
146 }
147}
148
149pub fn local_get(handle: Handle) -> Result<NodeId> {
151 let mut nodeid: u32 = 0;
152 let res = unsafe { ffi::corosync_cfg_local_get(handle.cfg_handle, &mut nodeid) };
153 if res == ffi::CS_OK {
154 Ok(NodeId::from(nodeid))
155 } else {
156 Err(CsError::from_c(res))
157 }
158}
159
160pub fn reload_cnfig(handle: Handle) -> Result<()> {
162 let res = unsafe { ffi::corosync_cfg_reload_config(handle.cfg_handle) };
163 if res == ffi::CS_OK {
164 Ok(())
165 } else {
166 Err(CsError::from_c(res))
167 }
168}
169
170pub fn reopen_log_files(handle: Handle) -> Result<()> {
172 let res = unsafe { ffi::corosync_cfg_reopen_log_files(handle.cfg_handle) };
173 if res == ffi::CS_OK {
174 Ok(())
175 } else {
176 Err(CsError::from_c(res))
177 }
178}
179
180pub fn kill_node(handle: Handle, nodeid: NodeId, reason: &str) -> Result<()> {
183 let c_string = {
184 match CString::new(reason) {
185 Ok(cs) => cs,
186 Err(_) => return Err(CsError::CsErrInvalidParam),
187 }
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
200pub fn try_shutdown(handle: Handle, flags: ShutdownFlags) -> Result<()> {
204 let c_flags = match flags {
205 ShutdownFlags::Request => 0,
206 ShutdownFlags::Regardless => 1,
207 ShutdownFlags::Immediate => 2,
208 };
209 let res = unsafe { ffi::corosync_cfg_try_shutdown(handle.cfg_handle, c_flags) };
210 if res == ffi::CS_OK {
211 Ok(())
212 } else {
213 Err(CsError::from_c(res))
214 }
215}
216
217pub fn reply_to_shutdown(handle: Handle, flags: ShutdownReply) -> Result<()> {
219 let c_flags = match flags {
220 ShutdownReply::No => 0,
221 ShutdownReply::Yes => 1,
222 };
223 let res = unsafe { ffi::corosync_cfg_replyto_shutdown(handle.cfg_handle, c_flags) };
224 if res == ffi::CS_OK {
225 Ok(())
226 } else {
227 Err(CsError::from_c(res))
228 }
229}
230
231pub fn dispatch(handle: Handle, flags: DispatchFlags) -> Result<()> {
233 let res = unsafe { ffi::corosync_cfg_dispatch(handle.cfg_handle, flags as u32) };
234 if res == ffi::CS_OK {
235 Ok(())
236 } else {
237 Err(CsError::from_c(res))
238 }
239}
240
241fn u8_to_bool(val: u8) -> bool {
243 val != 0
244}
245
246const CFG_MAX_LINKS: usize = 8;
247const CFG_MAX_HOST_LEN: usize = 256;
248fn unpack_nodestatus(c_nodestatus: ffi::corosync_cfg_node_status_v1) -> Result<NodeStatus> {
249 let mut ns = NodeStatus {
250 version: NodeStatusVersion::V1,
251 nodeid: NodeId::from(c_nodestatus.nodeid),
252 reachable: u8_to_bool(c_nodestatus.reachable),
253 remote: u8_to_bool(c_nodestatus.remote),
254 external: u8_to_bool(c_nodestatus.external),
255 onwire_min: c_nodestatus.onwire_min,
256 onwire_max: c_nodestatus.onwire_max,
257 onwire_ver: c_nodestatus.onwire_min,
258 link_status: Vec::<LinkStatus>::new(),
259 };
260 for i in 0..CFG_MAX_LINKS {
261 let ls = LinkStatus {
262 enabled: u8_to_bool(c_nodestatus.link_status[i].enabled),
263 connected: u8_to_bool(c_nodestatus.link_status[i].connected),
264 dynconnected: u8_to_bool(c_nodestatus.link_status[i].dynconnected),
265 mtu: c_nodestatus.link_status[i].mtu,
266 src_ipaddr: string_from_bytes(
267 &c_nodestatus.link_status[i].src_ipaddr[0],
268 CFG_MAX_HOST_LEN,
269 )?,
270 dst_ipaddr: string_from_bytes(
271 &c_nodestatus.link_status[i].dst_ipaddr[0],
272 CFG_MAX_HOST_LEN,
273 )?,
274 };
275 ns.link_status.push(ls);
276 }
277
278 Ok(ns)
279}
280
281fn new_ls() -> ffi::corosync_knet_link_status_v1 {
283 ffi::corosync_knet_link_status_v1 {
284 enabled: 0,
285 connected: 0,
286 dynconnected: 0,
287 mtu: 0,
288 src_ipaddr: [0; 256],
289 dst_ipaddr: [0; 256],
290 }
291}
292
293pub fn node_status_get(
296 handle: Handle,
297 nodeid: NodeId,
298 _version: NodeStatusVersion,
299) -> Result<NodeStatus> {
300 unsafe {
302 let mut c_nodestatus = ffi::corosync_cfg_node_status_v1 {
304 version: 1,
305 nodeid: 0,
306 reachable: 0,
307 remote: 0,
308 external: 0,
309 onwire_min: 0,
310 onwire_max: 0,
311 onwire_ver: 0,
312 link_status: [new_ls(); 8],
313 };
314
315 let res = ffi::corosync_cfg_node_status_get(
316 handle.cfg_handle,
317 u32::from(nodeid),
318 1,
319 &mut c_nodestatus as *mut _ as *mut c_void,
320 );
321
322 if res == ffi::CS_OK {
323 unpack_nodestatus(c_nodestatus)
324 } else {
325 Err(CsError::from_c(res))
326 }
327 }
328}
329
330pub fn track_start(handle: Handle, _flags: TrackFlags) -> Result<()> {
332 let res = unsafe { ffi::corosync_cfg_trackstart(handle.cfg_handle, 0) };
333 if res == ffi::CS_OK {
334 Ok(())
335 } else {
336 Err(CsError::from_c(res))
337 }
338}
339
340pub fn track_stop(handle: Handle) -> Result<()> {
342 let res = unsafe { ffi::corosync_cfg_trackstop(handle.cfg_handle) };
343 if res == ffi::CS_OK {
344 Ok(())
345 } else {
346 Err(CsError::from_c(res))
347 }
348}