Skip to main content

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