rust_corosync/
cmap.rs

1// libcmap 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
9#![allow(clippy::type_complexity)]
10
11// For the code generated by bindgen
12use crate::sys::cmap as ffi;
13
14use num_enum::TryFromPrimitive;
15use std::any::type_name;
16use std::collections::HashMap;
17use std::convert::TryFrom;
18use std::ffi::CString;
19use std::fmt;
20use std::os::raw::{c_char, c_int, c_void};
21use std::ptr::copy_nonoverlapping;
22use std::sync::Mutex;
23
24use crate::string_from_bytes;
25use crate::{CsError, DispatchFlags, Result};
26
27// Maps:
28/// "Maps" available to [initialize]
29pub enum Map {
30    Icmap,
31    Stats,
32}
33
34bitflags! {
35/// Tracker types for cmap, both passed into [track_add]
36/// and returned from its callback.
37    pub struct TrackType: i32
38    {
39    const DELETE = 1;
40    const MODIFY = 2;
41    const ADD = 4;
42    const PREFIX = 8;
43    }
44}
45
46impl fmt::Display for TrackType {
47    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
48        if self.contains(TrackType::DELETE) {
49            write!(f, "DELETE ")?
50        }
51        if self.contains(TrackType::MODIFY) {
52            write!(f, "MODIFY ")?
53        }
54        if self.contains(TrackType::ADD) {
55            write!(f, "ADD ")?
56        }
57        if self.contains(TrackType::PREFIX) {
58            write!(f, "PREFIX ")
59        } else {
60            Ok(())
61        }
62    }
63}
64
65#[derive(Copy, Clone)]
66/// A handle returned from [initialize], needs to be passed to all other cmap API calls
67pub struct Handle {
68    cmap_handle: u64,
69}
70
71#[derive(Copy, Clone)]
72/// A handle for a specific CMAP tracker. returned from [track_add].
73/// There may be multiple TrackHandles per [Handle]
74pub struct TrackHandle {
75    track_handle: u64,
76    notify_callback: NotifyCallback,
77}
78
79// Used to convert CMAP handles into one of ours, for callbacks
80lazy_static! {
81    static ref TRACKHANDLE_HASH: Mutex<HashMap<u64, TrackHandle>> = Mutex::new(HashMap::new());
82    static ref HANDLE_HASH: Mutex<HashMap<u64, Handle>> = Mutex::new(HashMap::new());
83}
84
85/// Initialize a connection to the cmap subsystem.
86/// map specifies which cmap "map" to use.
87/// Returns a [Handle] into the cmap library
88pub fn initialize(map: Map) -> Result<Handle> {
89    let mut handle: ffi::cmap_handle_t = 0;
90    let c_map = match map {
91        Map::Icmap => ffi::CMAP_MAP_ICMAP,
92        Map::Stats => ffi::CMAP_MAP_STATS,
93    };
94
95    unsafe {
96        let res = ffi::cmap_initialize_map(&mut handle, c_map);
97        if res == ffi::CS_OK {
98            let rhandle = Handle {
99                cmap_handle: handle,
100            };
101            HANDLE_HASH.lock().unwrap().insert(handle, rhandle);
102            Ok(rhandle)
103        } else {
104            Err(CsError::from_c(res))
105        }
106    }
107}
108
109/// Finish with a connection to corosync.
110/// Takes a [Handle] as returned from [initialize]
111pub fn finalize(handle: Handle) -> Result<()> {
112    let res = unsafe { ffi::cmap_finalize(handle.cmap_handle) };
113    if res == ffi::CS_OK {
114        HANDLE_HASH.lock().unwrap().remove(&handle.cmap_handle);
115        Ok(())
116    } else {
117        Err(CsError::from_c(res))
118    }
119}
120
121/// Return a file descriptor to use for poll/select on the CMAP handle.
122/// Takes a [Handle] as returned from [initialize],
123/// returns a C file descriptor as i32
124pub fn fd_get(handle: Handle) -> Result<i32> {
125    let c_fd: *mut c_int = &mut 0 as *mut _ as *mut c_int;
126    let res = unsafe { ffi::cmap_fd_get(handle.cmap_handle, c_fd) };
127    if res == ffi::CS_OK {
128        Ok(c_fd as i32)
129    } else {
130        Err(CsError::from_c(res))
131    }
132}
133
134/// Dispatch any/all active CMAP callbacks.
135/// Takes a [Handle] as returned from [initialize],
136/// flags [DispatchFlags] tells it how many items to dispatch before returning
137pub fn dispatch(handle: Handle, flags: DispatchFlags) -> Result<()> {
138    let res = unsafe { ffi::cmap_dispatch(handle.cmap_handle, flags as u32) };
139    if res == ffi::CS_OK {
140        Ok(())
141    } else {
142        Err(CsError::from_c(res))
143    }
144}
145
146/// Get the current 'context' value for this handle
147/// The context value is an arbitrary value that is always passed
148/// back to callbacks to help identify the source
149pub fn context_get(handle: Handle) -> Result<u64> {
150    let (res, context) = unsafe {
151        let mut context: u64 = 0;
152        let c_context: *mut c_void = &mut context as *mut _ as *mut c_void;
153        let r = ffi::cmap_context_get(handle.cmap_handle, c_context as *mut *const c_void);
154        (r, context)
155    };
156    if res == ffi::CS_OK {
157        Ok(context)
158    } else {
159        Err(CsError::from_c(res))
160    }
161}
162
163/// Set the current 'context' value for this handle
164/// The context value is an arbitrary value that is always passed
165/// back to callbacks to help identify the source.
166/// Normally this is set in [initialize], but this allows it to be changed
167pub fn context_set(handle: Handle, context: u64) -> Result<()> {
168    let res = unsafe {
169        let c_context = context as *mut c_void;
170        ffi::cmap_context_set(handle.cmap_handle, c_context)
171    };
172    if res == ffi::CS_OK {
173        Ok(())
174    } else {
175        Err(CsError::from_c(res))
176    }
177}
178
179/// The type of data returned from [get] or in a
180/// tracker callback or iterator, part of the [Data] struct
181#[derive(Clone, Copy, Debug, Eq, PartialEq, TryFromPrimitive)]
182#[repr(u32)]
183pub enum DataType {
184    Int8 = ffi::CMAP_VALUETYPE_INT8,
185    UInt8 = ffi::CMAP_VALUETYPE_UINT8,
186    Int16 = ffi::CMAP_VALUETYPE_INT16,
187    UInt16 = ffi::CMAP_VALUETYPE_UINT16,
188    Int32 = ffi::CMAP_VALUETYPE_INT32,
189    UInt32 = ffi::CMAP_VALUETYPE_UINT32,
190    Int64 = ffi::CMAP_VALUETYPE_INT64,
191    UInt64 = ffi::CMAP_VALUETYPE_UINT64,
192    Float = ffi::CMAP_VALUETYPE_FLOAT,
193    Double = ffi::CMAP_VALUETYPE_DOUBLE,
194    String = ffi::CMAP_VALUETYPE_STRING,
195    Binary = ffi::CMAP_VALUETYPE_BINARY,
196    Unknown = 999,
197}
198
199fn cmap_to_enum(cmap_type: u32) -> DataType {
200    match DataType::try_from(cmap_type) {
201        Ok(e) => e,
202        Err(_) => DataType::Unknown,
203    }
204}
205
206/// Data returned from the cmap::get() call and tracker & iterators.
207/// Contains the data itself and the type of that data.
208pub enum Data {
209    Int8(i8),
210    UInt8(u8),
211    Int16(i16),
212    UInt16(u16),
213    Int32(i32),
214    UInt32(u32),
215    Int64(i64),
216    UInt64(u64),
217    Float(f32),
218    Double(f64),
219    String(String),
220    Binary(Vec<u8>),
221    Unknown,
222}
223
224impl fmt::Display for DataType {
225    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
226        match self {
227            DataType::Int8 => write!(f, "Int8"),
228            DataType::UInt8 => write!(f, "UInt8"),
229            DataType::Int16 => write!(f, "Int16"),
230            DataType::UInt16 => write!(f, "UInt16"),
231            DataType::Int32 => write!(f, "Int32"),
232            DataType::UInt32 => write!(f, "UInt32"),
233            DataType::Int64 => write!(f, "Int64"),
234            DataType::UInt64 => write!(f, "UInt64"),
235            DataType::Float => write!(f, "Float"),
236            DataType::Double => write!(f, "Double"),
237            DataType::String => write!(f, "String"),
238            DataType::Binary => write!(f, "Binary"),
239            DataType::Unknown => write!(f, "Unknown"),
240        }
241    }
242}
243
244impl fmt::Display for Data {
245    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
246        match self {
247            Data::Int8(v) => write!(f, "{} (Int8)", v),
248            Data::UInt8(v) => write!(f, "{} (UInt8)", v),
249            Data::Int16(v) => write!(f, "{} (Int16)", v),
250            Data::UInt16(v) => write!(f, "{} (UInt16)", v),
251            Data::Int32(v) => write!(f, "{} (Int32)", v),
252            Data::UInt32(v) => write!(f, "{} (UInt32)", v),
253            Data::Int64(v) => write!(f, "{} (Int64)", v),
254            Data::UInt64(v) => write!(f, "{} (UInt64)", v),
255            Data::Float(v) => write!(f, "{} (Float)", v),
256            Data::Double(v) => write!(f, "{} (Double)", v),
257            Data::String(v) => write!(f, "{} (String)", v),
258            Data::Binary(v) => write!(f, "{:?} (Binary)", v),
259            Data::Unknown => write!(f, "Unknown)"),
260        }
261    }
262}
263
264const CMAP_KEYNAME_MAXLENGTH: usize = 255;
265fn string_to_cstring_validated(key: &str, maxlen: usize) -> Result<CString> {
266    if maxlen > 0 && key.chars().count() >= maxlen {
267        return Err(CsError::CsErrInvalidParam);
268    }
269
270    match CString::new(key) {
271        Ok(n) => Ok(n),
272        Err(_) => Err(CsError::CsErrLibrary),
273    }
274}
275
276fn set_value(
277    handle: Handle,
278    key_name: &str,
279    datatype: DataType,
280    value: *mut c_void,
281    length: usize,
282) -> Result<()> {
283    let csname = string_to_cstring_validated(key_name, CMAP_KEYNAME_MAXLENGTH)?;
284    let res = unsafe {
285        ffi::cmap_set(
286            handle.cmap_handle,
287            csname.as_ptr(),
288            value,
289            length,
290            datatype as u32,
291        )
292    };
293    if res == ffi::CS_OK {
294        Ok(())
295    } else {
296        Err(CsError::from_c(res))
297    }
298}
299
300// Returns type and size
301fn generic_to_cmap<T>(_value: T) -> (DataType, usize) {
302    match type_name::<T>() {
303        "u8" => (DataType::UInt8, 1),
304        "i8" => (DataType::Int8, 1),
305        "u16" => (DataType::UInt16, 2),
306        "i16" => (DataType::Int16, 2),
307        "u32" => (DataType::UInt32, 4),
308        "i32" => (DataType::Int32, 4),
309        "u64" => (DataType::UInt64, 4),
310        "f32" => (DataType::Float, 4),
311        "f64" => (DataType::Double, 8),
312        "&str" => (DataType::String, 0),
313        // Binary not currently supported here
314        _ => (DataType::Unknown, 0),
315    }
316}
317
318fn is_numeric_type(dtype: DataType) -> bool {
319    matches!(
320        dtype,
321        DataType::UInt8
322            | DataType::Int8
323            | DataType::UInt16
324            | DataType::Int16
325            | DataType::UInt32
326            | DataType::Int32
327            | DataType::UInt64
328            | DataType::Int64
329            | DataType::Float
330            | DataType::Double
331    )
332}
333
334/// Function to set a generic numeric value
335/// This doesn't work for strings or binaries
336pub fn set_number<T: Copy>(handle: Handle, key_name: &str, value: T) -> Result<()> {
337    let (c_type, c_size) = generic_to_cmap(value);
338
339    if is_numeric_type(c_type) {
340        let mut tmp = value;
341        let c_value: *mut c_void = &mut tmp as *mut _ as *mut c_void;
342        set_value(handle, key_name, c_type, c_value as *mut c_void, c_size)
343    } else {
344        Err(CsError::CsErrNotSupported)
345    }
346}
347
348pub fn set_u8(handle: Handle, key_name: &str, value: u8) -> Result<()> {
349    let mut tmp = value;
350    let c_value: *mut c_void = &mut tmp as *mut _ as *mut c_void;
351    set_value(handle, key_name, DataType::UInt8, c_value as *mut c_void, 1)
352}
353
354/// Sets an i8 value into cmap
355pub fn set_i8(handle: Handle, key_name: &str, value: i8) -> Result<()> {
356    let mut tmp = value;
357    let c_value: *mut c_void = &mut tmp as *mut _ as *mut c_void;
358    set_value(handle, key_name, DataType::Int8, c_value as *mut c_void, 1)
359}
360
361/// Sets a u16 value into cmap
362pub fn set_u16(handle: Handle, key_name: &str, value: u16) -> Result<()> {
363    let mut tmp = value;
364    let c_value: *mut c_void = &mut tmp as *mut _ as *mut c_void;
365    set_value(
366        handle,
367        key_name,
368        DataType::UInt16,
369        c_value as *mut c_void,
370        2,
371    )
372}
373
374/// Sets an i16 value into cmap
375pub fn set_i16(handle: Handle, key_name: &str, value: i16) -> Result<()> {
376    let mut tmp = value;
377    let c_value: *mut c_void = &mut tmp as *mut _ as *mut c_void;
378    set_value(handle, key_name, DataType::Int16, c_value as *mut c_void, 2)
379}
380
381/// Sets a u32 value into cmap
382pub fn set_u32(handle: Handle, key_name: &str, value: u32) -> Result<()> {
383    let mut tmp = value;
384    let c_value: *mut c_void = &mut tmp as *mut _ as *mut c_void;
385    set_value(handle, key_name, DataType::UInt32, c_value, 4)
386}
387
388/// Sets an i32 value into cmap
389pub fn set_i132(handle: Handle, key_name: &str, value: i32) -> Result<()> {
390    let mut tmp = value;
391    let c_value: *mut c_void = &mut tmp as *mut _ as *mut c_void;
392    set_value(handle, key_name, DataType::Int32, c_value as *mut c_void, 4)
393}
394
395/// Sets a u64 value into cmap
396pub fn set_u64(handle: Handle, key_name: &str, value: u64) -> Result<()> {
397    let mut tmp = value;
398    let c_value: *mut c_void = &mut tmp as *mut _ as *mut c_void;
399    set_value(
400        handle,
401        key_name,
402        DataType::UInt64,
403        c_value as *mut c_void,
404        8,
405    )
406}
407
408/// Sets an i64 value into cmap
409pub fn set_i164(handle: Handle, key_name: &str, value: i64) -> Result<()> {
410    let mut tmp = value;
411    let c_value: *mut c_void = &mut tmp as *mut _ as *mut c_void;
412    set_value(handle, key_name, DataType::Int64, c_value as *mut c_void, 8)
413}
414
415/// Sets a string value into cmap
416pub fn set_string(handle: Handle, key_name: &str, value: &str) -> Result<()> {
417    let v_string = string_to_cstring_validated(value, 0)?;
418    set_value(
419        handle,
420        key_name,
421        DataType::String,
422        v_string.as_ptr() as *mut c_void,
423        value.chars().count(),
424    )
425}
426
427/// Sets a binary value into cmap
428pub fn set_binary(handle: Handle, key_name: &str, value: &[u8]) -> Result<()> {
429    set_value(
430        handle,
431        key_name,
432        DataType::Binary,
433        value.as_ptr() as *mut c_void,
434        value.len(),
435    )
436}
437
438/// Sets a [Data] type into cmap
439pub fn set(handle: Handle, key_name: &str, data: &Data) -> Result<()> {
440    let (datatype, datalen, c_value) = match data {
441        Data::Int8(v) => {
442            let mut tmp = *v;
443            let cv: *mut c_void = &mut tmp as *mut _ as *mut c_void;
444            (DataType::Int8, 1, cv)
445        }
446        Data::UInt8(v) => {
447            let mut tmp = *v;
448            let cv: *mut c_void = &mut tmp as *mut _ as *mut c_void;
449            (DataType::UInt8, 1, cv)
450        }
451        Data::Int16(v) => {
452            let mut tmp = *v;
453            let cv: *mut c_void = &mut tmp as *mut _ as *mut c_void;
454            (DataType::Int16, 2, cv)
455        }
456        Data::UInt16(v) => {
457            let mut tmp = *v;
458            let cv: *mut c_void = &mut tmp as *mut _ as *mut c_void;
459            (DataType::UInt8, 2, cv)
460        }
461        Data::Int32(v) => {
462            let mut tmp = *v;
463            let cv: *mut c_void = &mut tmp as *mut _ as *mut c_void;
464            (DataType::Int32, 4, cv)
465        }
466        Data::UInt32(v) => {
467            let mut tmp = *v;
468            let cv: *mut c_void = &mut tmp as *mut _ as *mut c_void;
469            (DataType::UInt32, 4, cv)
470        }
471        Data::Int64(v) => {
472            let mut tmp = *v;
473            let cv: *mut c_void = &mut tmp as *mut _ as *mut c_void;
474            (DataType::Int64, 8, cv)
475        }
476        Data::UInt64(v) => {
477            let mut tmp = *v;
478            let cv: *mut c_void = &mut tmp as *mut _ as *mut c_void;
479            (DataType::UInt64, 8, cv)
480        }
481        Data::Float(v) => {
482            let mut tmp = *v;
483            let cv: *mut c_void = &mut tmp as *mut _ as *mut c_void;
484            (DataType::Float, 4, cv)
485        }
486        Data::Double(v) => {
487            let mut tmp = *v;
488            let cv: *mut c_void = &mut tmp as *mut _ as *mut c_void;
489            (DataType::Double, 8, cv)
490        }
491        Data::String(v) => {
492            let cv = string_to_cstring_validated(v, 0)?;
493            // Can't let cv go out of scope
494            return set_value(
495                handle,
496                key_name,
497                DataType::String,
498                cv.as_ptr() as *mut c_void,
499                v.chars().count(),
500            );
501        }
502        Data::Binary(v) => {
503            // Vec doesn't return quite the right types.
504            return set_value(
505                handle,
506                key_name,
507                DataType::Binary,
508                v.as_ptr() as *mut c_void,
509                v.len(),
510            );
511        }
512        Data::Unknown => return Err(CsError::CsErrInvalidParam),
513    };
514
515    set_value(handle, key_name, datatype, c_value, datalen)
516}
517
518// Local function to parse out values from the C mess
519// Assumes the c_value is complete. So cmap::get() will need to check the size
520//   and re-get before calling us with a resized buffer
521fn c_to_data(value_size: usize, c_key_type: u32, c_value: *const u8) -> Result<Data> {
522    unsafe {
523        match cmap_to_enum(c_key_type) {
524            DataType::UInt8 => {
525                let mut ints = [0u8; 1];
526                copy_nonoverlapping(c_value as *mut u8, ints.as_mut_ptr() as *mut u8, value_size);
527                Ok(Data::UInt8(ints[0]))
528            }
529            DataType::Int8 => {
530                let mut ints = [0i8; 1];
531                copy_nonoverlapping(c_value as *mut u8, ints.as_mut_ptr() as *mut u8, value_size);
532                Ok(Data::Int8(ints[0]))
533            }
534            DataType::UInt16 => {
535                let mut ints = [0u16; 1];
536                copy_nonoverlapping(c_value as *mut u8, ints.as_mut_ptr() as *mut u8, value_size);
537                Ok(Data::UInt16(ints[0]))
538            }
539            DataType::Int16 => {
540                let mut ints = [0i16; 1];
541                copy_nonoverlapping(c_value as *mut u8, ints.as_mut_ptr() as *mut u8, value_size);
542                Ok(Data::Int16(ints[0]))
543            }
544            DataType::UInt32 => {
545                let mut ints = [0u32; 1];
546                copy_nonoverlapping(c_value as *mut u8, ints.as_mut_ptr() as *mut u8, value_size);
547                Ok(Data::UInt32(ints[0]))
548            }
549            DataType::Int32 => {
550                let mut ints = [0i32; 1];
551                copy_nonoverlapping(c_value as *mut u8, ints.as_mut_ptr() as *mut u8, value_size);
552                Ok(Data::Int32(ints[0]))
553            }
554            DataType::UInt64 => {
555                let mut ints = [0u64; 1];
556                copy_nonoverlapping(c_value as *mut u8, ints.as_mut_ptr() as *mut u8, value_size);
557                Ok(Data::UInt64(ints[0]))
558            }
559            DataType::Int64 => {
560                let mut ints = [0i64; 1];
561                copy_nonoverlapping(c_value as *mut u8, ints.as_mut_ptr() as *mut u8, value_size);
562                Ok(Data::Int64(ints[0]))
563            }
564            DataType::Float => {
565                let mut ints = [0f32; 1];
566                copy_nonoverlapping(c_value as *mut u8, ints.as_mut_ptr() as *mut u8, value_size);
567                Ok(Data::Float(ints[0]))
568            }
569            DataType::Double => {
570                let mut ints = [0f64; 1];
571                copy_nonoverlapping(c_value as *mut u8, ints.as_mut_ptr() as *mut u8, value_size);
572                Ok(Data::Double(ints[0]))
573            }
574            DataType::String => {
575                let mut ints = Vec::<u8>::new();
576                ints.resize(value_size, 0u8);
577                copy_nonoverlapping(c_value as *mut u8, ints.as_mut_ptr() as *mut u8, value_size);
578                // -1 here so CString doesn't see the NUL
579                let cs = match CString::new(&ints[0..value_size - 1_usize]) {
580                    Ok(c1) => c1,
581                    Err(_) => return Err(CsError::CsErrLibrary),
582                };
583                match cs.into_string() {
584                    Ok(s) => Ok(Data::String(s)),
585                    Err(_) => Err(CsError::CsErrLibrary),
586                }
587            }
588            DataType::Binary => {
589                let mut ints = Vec::<u8>::new();
590                ints.resize(value_size, 0u8);
591                copy_nonoverlapping(c_value as *mut u8, ints.as_mut_ptr() as *mut u8, value_size);
592                Ok(Data::Binary(ints))
593            }
594            DataType::Unknown => Ok(Data::Unknown),
595        }
596    }
597}
598
599const INITIAL_SIZE: usize = 256;
600
601/// Get a value from cmap, returned as a [Data] struct, so could be anything
602pub fn get(handle: Handle, key_name: &str) -> Result<Data> {
603    let csname = string_to_cstring_validated(key_name, CMAP_KEYNAME_MAXLENGTH)?;
604    let mut value_size: usize = 16;
605    let mut c_key_type: u32 = 0;
606    let mut c_value = Vec::<u8>::new();
607
608    // First guess at a size for Strings and Binaries. Expand if needed
609    c_value.resize(INITIAL_SIZE, 0u8);
610
611    unsafe {
612        let res = ffi::cmap_get(
613            handle.cmap_handle,
614            csname.as_ptr(),
615            c_value.as_mut_ptr() as *mut c_void,
616            &mut value_size,
617            &mut c_key_type,
618        );
619        if res == ffi::CS_OK {
620            if value_size > INITIAL_SIZE {
621                // Need to try again with a bigger buffer
622                c_value.resize(value_size, 0u8);
623                let res2 = ffi::cmap_get(
624                    handle.cmap_handle,
625                    csname.as_ptr(),
626                    c_value.as_mut_ptr() as *mut c_void,
627                    &mut value_size,
628                    &mut c_key_type,
629                );
630                if res2 != ffi::CS_OK {
631                    return Err(CsError::from_c(res2));
632                }
633            }
634
635            // Convert to Rust type and return as a Data enum
636            c_to_data(value_size, c_key_type, c_value.as_ptr())
637        } else {
638            Err(CsError::from_c(res))
639        }
640    }
641}
642
643/// increment the value in a cmap key (must be a numeric type)
644pub fn inc(handle: Handle, key_name: &str) -> Result<()> {
645    let csname = string_to_cstring_validated(key_name, CMAP_KEYNAME_MAXLENGTH)?;
646    let res = unsafe { ffi::cmap_inc(handle.cmap_handle, csname.as_ptr()) };
647    if res == ffi::CS_OK {
648        Ok(())
649    } else {
650        Err(CsError::from_c(res))
651    }
652}
653
654/// decrement the value in a cmap key (must be a numeric type)
655pub fn dec(handle: Handle, key_name: &str) -> Result<()> {
656    let csname = string_to_cstring_validated(key_name, CMAP_KEYNAME_MAXLENGTH)?;
657    let res = unsafe { ffi::cmap_dec(handle.cmap_handle, csname.as_ptr()) };
658    if res == ffi::CS_OK {
659        Ok(())
660    } else {
661        Err(CsError::from_c(res))
662    }
663}
664
665// Callback for CMAP notify events from corosync, convert params to Rust and pass on.
666extern "C" fn rust_notify_fn(
667    cmap_handle: ffi::cmap_handle_t,
668    cmap_track_handle: ffi::cmap_track_handle_t,
669    event: i32,
670    key_name: *const ::std::os::raw::c_char,
671    new_value: ffi::cmap_notify_value,
672    old_value: ffi::cmap_notify_value,
673    user_data: *mut ::std::os::raw::c_void,
674) {
675    // If cmap_handle doesn't match then throw away the callback.
676    if let Some(r_cmap_handle) = HANDLE_HASH.lock().unwrap().get(&cmap_handle) {
677        if let Some(h) = TRACKHANDLE_HASH.lock().unwrap().get(&cmap_track_handle) {
678            let r_keyname = match string_from_bytes(key_name, CMAP_KEYNAME_MAXLENGTH) {
679                Ok(s) => s,
680                Err(_) => return,
681            };
682
683            let r_old = match c_to_data(old_value.len, old_value.type_, old_value.data as *const u8)
684            {
685                Ok(v) => v,
686                Err(_) => return,
687            };
688            let r_new = match c_to_data(new_value.len, new_value.type_, new_value.data as *const u8)
689            {
690                Ok(v) => v,
691                Err(_) => return,
692            };
693
694            if let Some(cb) = h.notify_callback.notify_fn {
695                (cb)(
696                    r_cmap_handle,
697                    h,
698                    TrackType { bits: event },
699                    &r_keyname,
700                    &r_old,
701                    &r_new,
702                    user_data as u64,
703                );
704            }
705        }
706    }
707}
708
709/// Callback function called every time a tracker reports a change in a tracked value
710#[derive(Copy, Clone)]
711pub struct NotifyCallback {
712    pub notify_fn: Option<
713        fn(
714            handle: &Handle,
715            track_handle: &TrackHandle,
716            event: TrackType,
717            key_name: &str,
718            new_value: &Data,
719            old_value: &Data,
720            user_data: u64,
721        ),
722    >,
723}
724
725/// Track changes in cmap values, multiple [TrackHandle]s per [Handle] are allowed
726pub fn track_add(
727    handle: Handle,
728    key_name: &str,
729    track_type: TrackType,
730    notify_callback: &NotifyCallback,
731    user_data: u64,
732) -> Result<TrackHandle> {
733    let c_name = string_to_cstring_validated(key_name, CMAP_KEYNAME_MAXLENGTH)?;
734    let mut c_trackhandle = 0u64;
735    let res = unsafe {
736        ffi::cmap_track_add(
737            handle.cmap_handle,
738            c_name.as_ptr(),
739            track_type.bits,
740            Some(rust_notify_fn),
741            user_data as *mut c_void,
742            &mut c_trackhandle,
743        )
744    };
745    if res == ffi::CS_OK {
746        let rhandle = TrackHandle {
747            track_handle: c_trackhandle,
748            notify_callback: *notify_callback,
749        };
750        TRACKHANDLE_HASH
751            .lock()
752            .unwrap()
753            .insert(c_trackhandle, rhandle);
754        Ok(rhandle)
755    } else {
756        Err(CsError::from_c(res))
757    }
758}
759
760/// Remove a tracker frm this [Handle]
761pub fn track_delete(handle: Handle, track_handle: TrackHandle) -> Result<()> {
762    let res = unsafe { ffi::cmap_track_delete(handle.cmap_handle, track_handle.track_handle) };
763    if res == ffi::CS_OK {
764        TRACKHANDLE_HASH
765            .lock()
766            .unwrap()
767            .remove(&track_handle.track_handle);
768        Ok(())
769    } else {
770        Err(CsError::from_c(res))
771    }
772}
773
774/// Create one of these to start iterating over cmap values.
775pub struct CmapIterStart {
776    iter_handle: u64,
777    cmap_handle: u64,
778}
779
780pub struct CmapIntoIter {
781    cmap_handle: u64,
782    iter_handle: u64,
783}
784
785/// Value returned from the iterator. contains the key name and the [Data]
786pub struct CmapIter {
787    key_name: String,
788    data: Data,
789}
790
791impl CmapIter {
792    pub fn key_name(&self) -> &str {
793        &self.key_name
794    }
795    pub fn data(&self) -> &Data {
796        &self.data
797    }
798}
799
800impl fmt::Debug for CmapIter {
801    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
802        write!(f, "{}: {}", self.key_name, self.data)
803    }
804}
805
806impl Iterator for CmapIntoIter {
807    type Item = CmapIter;
808
809    fn next(&mut self) -> Option<CmapIter> {
810        let mut c_key_name = [0u8; CMAP_KEYNAME_MAXLENGTH + 1];
811        let mut c_value_len = 0usize;
812        let mut c_value_type = 0u32;
813        let res = unsafe {
814            ffi::cmap_iter_next(
815                self.cmap_handle,
816                self.iter_handle,
817                c_key_name.as_mut_ptr() as *mut c_char,
818                &mut c_value_len,
819                &mut c_value_type,
820            )
821        };
822        if res == ffi::CS_OK {
823            // Return the Data for this iteration
824            let mut c_value = Vec::<u8>::new();
825            c_value.resize(c_value_len, 0u8);
826            let res = unsafe {
827                ffi::cmap_get(
828                    self.cmap_handle,
829                    c_key_name.as_ptr() as *mut c_char,
830                    c_value.as_mut_ptr() as *mut c_void,
831                    &mut c_value_len,
832                    &mut c_value_type,
833                )
834            };
835            if res == ffi::CS_OK {
836                match c_to_data(c_value_len, c_value_type, c_value.as_ptr()) {
837                    Ok(d) => {
838                        let r_keyname = match string_from_bytes(
839                            c_key_name.as_ptr() as *mut c_char,
840                            CMAP_KEYNAME_MAXLENGTH,
841                        ) {
842                            Ok(s) => s,
843                            Err(_) => return None,
844                        };
845                        Some(CmapIter {
846                            key_name: r_keyname,
847                            data: d,
848                        })
849                    }
850                    Err(_) => None,
851                }
852            } else {
853                // cmap_get returned error
854                None
855            }
856        } else if res == ffi::CS_ERR_NO_SECTIONS {
857            // End of list
858            unsafe {
859                // Yeah, we don't check this return code. There's nowhere to report it.
860                ffi::cmap_iter_finalize(self.cmap_handle, self.iter_handle)
861            };
862            None
863        } else {
864            None
865        }
866    }
867}
868
869impl CmapIterStart {
870    /// Create a new [CmapIterStart] object for iterating over a list of cmap keys
871    pub fn new(cmap_handle: Handle, prefix: &str) -> Result<CmapIterStart> {
872        let mut iter_handle: u64 = 0;
873        let res = unsafe {
874            let c_prefix = string_to_cstring_validated(prefix, CMAP_KEYNAME_MAXLENGTH)?;
875            ffi::cmap_iter_init(cmap_handle.cmap_handle, c_prefix.as_ptr(), &mut iter_handle)
876        };
877        if res == ffi::CS_OK {
878            Ok(CmapIterStart {
879                cmap_handle: cmap_handle.cmap_handle,
880                iter_handle,
881            })
882        } else {
883            Err(CsError::from_c(res))
884        }
885    }
886}
887
888impl IntoIterator for CmapIterStart {
889    type Item = CmapIter;
890    type IntoIter = CmapIntoIter;
891
892    fn into_iter(self) -> Self::IntoIter {
893        CmapIntoIter {
894            iter_handle: self.iter_handle,
895            cmap_handle: self.cmap_handle,
896        }
897    }
898}