kpal_plugin/
lib.rs

1//! The KPAL plugin crate provides tools to write your own KPAL plugins.
2//!
3//! See the examples folder for ideas on how to implement the datatypes and methods defined in this
4//! library.
5mod constants;
6mod errors;
7mod ffi;
8mod strings;
9
10use std::{
11    cell::{Ref, RefCell},
12    cmp::PartialEq,
13    error::Error,
14    ffi::{CStr, CString, FromBytesWithNulError},
15    fmt, slice,
16};
17
18use libc::{c_char, c_double, c_int, c_uchar, c_uint, size_t};
19pub use multi_map::{multimap, MultiMap};
20
21pub use {
22    constants::*,
23    errors::error_codes,
24    errors::{PluginUninitializedError, ERRORS},
25    ffi::*,
26    strings::copy_string,
27};
28
29/// The set of functions that must be implemented by a plugin.
30pub trait PluginAPI<E: Error + PluginError + 'static>
31where
32    Self: Sized,
33{
34    /// Returns a new instance of the plugin. No initialization of the hardware is performed.
35    fn new() -> Result<Self, E>;
36
37    /// Initialzes the plugin by performing any hardware initialization.
38    fn init(&mut self) -> Result<(), E>;
39
40    /// Returns the attributes of the plugin.
41    fn attributes(&self) -> &Attributes<Self, E>;
42
43    /// Returns the number of attributes of the plugin.
44    fn attribute_count(&self) -> usize {
45        self.attributes().borrow().iter().count()
46    }
47
48    /// Returns the attribute IDs.
49    fn attribute_ids(&self) -> Vec<usize> {
50        self.attributes()
51            .borrow()
52            .iter()
53            .map(|(id, _)| *id)
54            .collect()
55    }
56
57    /// Returns the name of an attribute.
58    ///
59    /// If the attribute that corresponds to the `id` does not exist, then an error is
60    /// returned. Otherwise, the name is returned as a C-compatible `&CStr`.
61    ///
62    /// # Arguments
63    ///
64    /// * `id` - the numeric ID of the attribute
65    fn attribute_name(&self, id: usize) -> Result<Ref<CString>, E> {
66        log::debug!("Received request for the name of attribute: {}", id);
67        let attributes = self.attributes().borrow();
68        match attributes.get(&id) {
69            Some(_) => Ok(Ref::map(attributes, |a| {
70                &a.get(&id)
71                    .expect("Attribute does not exist. This should never happen.")
72                    .name
73            })),
74            None => Err(E::new(error_codes::ATTRIBUTE_DOES_NOT_EXIST)),
75        }
76    }
77
78    /// Indicates whether an attribute may be set before initialization.
79    ///
80    /// # Arguments
81    ///
82    /// # `id` - the numeric ID of the attribute
83    fn attribute_pre_init(&self, id: usize) -> Result<bool, E> {
84        log::debug!(
85            "Received request for attribute pre-initialzation status: {}",
86            id
87        );
88        let attributes = self.attributes();
89        let attributes = attributes.borrow();
90        let attribute = attributes
91            .get(&id)
92            .ok_or_else(|| E::new(error_codes::ATTRIBUTE_DOES_NOT_EXIST))?;
93
94        match attribute.callbacks_init {
95            Callbacks::Update => Ok(true),
96            _ => Ok(false),
97        }
98    }
99
100    /// Returns the value of an attribute.
101    ///
102    /// If the attribute that corresponds to the `id` does not exist, then an error is
103    /// returned. Otherwise, the value is returnd as a C-compatible tagged enum.
104    ///
105    /// # Arguments
106    ///
107    /// * `id` - the numeric ID of the attribute
108    /// * `phase` - the lifecycle phase of the plugin that determines which callbacks to use
109    fn attribute_value(&self, id: usize, phase: Phase) -> Result<Val, E> {
110        log::debug!("Received request for the value of attribute: {}", id);
111        let attributes = self.attributes();
112        let mut attributes = attributes.borrow_mut();
113        let attribute = attributes
114            .get_mut(&id)
115            .ok_or_else(|| E::new(error_codes::ATTRIBUTE_DOES_NOT_EXIST))?;
116
117        let get = if phase == constants::INIT_PHASE {
118            match attribute.callbacks_init {
119                Callbacks::Constant => return Ok(attribute.value.as_val()),
120                Callbacks::Update => return Ok(attribute.value.as_val()),
121                Callbacks::Get(get) => get,
122                Callbacks::GetAndSet(get, _) => get,
123            }
124        } else if phase == constants::RUN_PHASE {
125            match attribute.callbacks_run {
126                Callbacks::Constant => return Ok(attribute.value.as_val()),
127                Callbacks::Update => return Ok(attribute.value.as_val()),
128                Callbacks::Get(get) => get,
129                Callbacks::GetAndSet(get, _) => get,
130            }
131        } else {
132            return Err(E::new(error_codes::LIFECYCLE_PHASE_ERR));
133        };
134
135        let value = get(&self, &attribute.value).map_err(|err| {
136            log::error!("Callback error {{ id: {:?}, error: {:?} }}", id, err);
137            E::new(error_codes::CALLBACK_ERR)
138        })?;
139
140        // Update the attribute's cached value.
141        attribute.value = value;
142
143        Ok(attribute.value.as_val())
144    }
145
146    /// Sets the value of the attribute given by the id.
147    ///
148    /// If the attribute that corresponds to the `id` does not exist, or if the attribute cannot be
149    /// set, then an error is returned.
150    ///
151    /// # Arguments
152    ///
153    /// * `id` - the numeric ID of the attribute
154    /// * `val` - a reference to a Val instance
155    /// * `phase` - the lifecycle phase of the plugin that determines which callbacks to use
156    fn attribute_set_value(&self, id: usize, val: &Val, phase: Phase) -> Result<(), E> {
157        log::debug!("Received request to set the value of attribute: {}", id);
158        let attributes = self.attributes();
159        let mut attributes = attributes.borrow_mut();
160        let attribute = attributes
161            .get_mut(&id)
162            .ok_or_else(|| E::new(error_codes::ATTRIBUTE_DOES_NOT_EXIST))?;
163
164        let option_set = if phase == constants::INIT_PHASE {
165            match attribute.callbacks_init {
166                Callbacks::Update => None,
167                Callbacks::GetAndSet(_, set) => Some(set),
168                _ => return Err(E::new(error_codes::ATTRIBUTE_IS_NOT_SETTABLE)),
169            }
170        } else if phase == constants::RUN_PHASE {
171            match attribute.callbacks_run {
172                Callbacks::Update => None,
173                Callbacks::GetAndSet(_, set) => Some(set),
174                _ => return Err(E::new(error_codes::ATTRIBUTE_IS_NOT_SETTABLE)),
175            }
176        } else {
177            return Err(E::new(error_codes::LIFECYCLE_PHASE_ERR));
178        };
179
180        if let Some(set) = option_set {
181            // This MUST be updated each time a new variant is added to the Value and Val enums.
182            let result = match (&attribute.value, &val) {
183                (Value::Int(_), Val::Int(_))
184                | (Value::Double(_), Val::Double(_))
185                | (Value::String(_), Val::String(_, _))
186                | (Value::Uint(_), Val::Uint(_)) => set(&self, &attribute.value, val),
187                _ => Err(E::new(error_codes::ATTRIBUTE_TYPE_MISMATCH)),
188            };
189
190            result.map_err(|err| {
191                log::error!("Callback error {{ id: {:?}, error: {:?} }}", id, err);
192                E::new(error_codes::CALLBACK_ERR)
193            })?;
194        };
195
196        // Update the attribute's cached value.
197        attribute.value = val.to_value().map_err(|err| {
198            log::error!(
199                "Could not update plugin attribute's cached value: {{ id: {:?}, error: {:?} }}",
200                id,
201                err
202            );
203            E::new(error_codes::UPDATE_CACHED_VALUE_ERR)
204        })?;
205
206        Ok(())
207    }
208}
209
210/// The set of functions that must be implemented by a plugin library's main error type.
211pub trait PluginError: std::error::Error {
212    /// Initializes and returns a new instace of the error type.
213    ///
214    /// # Arguments
215    ///
216    /// * `error_code` - One of the integer error codes recognized by KPAL.
217    fn new(error_code: c_int) -> Self;
218
219    /// Returns the error code of the instance.
220    fn error_code(&self) -> c_int;
221}
222
223/// A Plugin combines the data that determines its state and with its functionality.
224///
225/// This struct holds a raw pointer to a data struct that is created by the plugin library. In
226/// addition, it contains the vtable of function pointers defined by the C API and implemented
227/// within the plugin library.
228///
229/// # Safety
230///
231/// The plugin implements the `Send` trait because after creation the plugin is moved into the
232/// thread that is dedicated to the plugin that it manages. Once it is moved, it will only ever be
233/// owned and used by this single thread by design.
234#[derive(Clone, Debug)]
235#[repr(C)]
236pub struct Plugin {
237    /// A pointer to a struct containing the state of the plugin.
238    pub plugin_data: *mut PluginData,
239
240    /// The table of function pointers that define part of the plugin API.
241    pub vtable: VTable,
242}
243
244impl Drop for Plugin {
245    /// Frees the memory allocated to the plugin data.
246    fn drop(&mut self) {
247        (self.vtable.plugin_free)(self.plugin_data);
248    }
249}
250
251unsafe impl Send for Plugin {}
252
253/// An opaque struct that contains the state of an individual plugin.
254///
255/// The daemon does not actually work directly with structs provided by a plugin library. Instead,
256/// they are hidden behind pointers to opaque structs of this type. The kpal-plugin FFI code takes
257/// care of casting the pointers back into the appropriate type inside the library code.
258///
259/// # Notes
260///
261/// In Rust, an opaque struct is defined as a struct with a field that is a zero-length array of
262/// unsigned 8-bit integers. It is used to hide the plugin's state, forcing all interactions
263/// with the data through the functions in the vtable instead.
264#[derive(Debug)]
265#[repr(C)]
266pub struct PluginData {
267    _private: [u8; 0],
268}
269
270/// A table of function pointers that comprise the plugin API for the foreign function interface.
271///
272/// By default, functions in the VTable return a number that represents a status code that maps
273/// onto a particular reason for an error. All functions should use the same mapping between status
274/// code and reason.
275///
276/// Functions that return values that do not represent status codes have names that end in the
277/// characters '_ns' that stand for "no status."
278#[derive(Clone, Debug)]
279#[repr(C)]
280pub struct VTable {
281    /// Frees the memory associated with a plugin's data.
282    pub plugin_free: extern "C" fn(*mut PluginData),
283
284    /// Initializes a plugin.
285    ///
286    /// This method is distinct from the `kpal_plugin_new` FFI call in that it actually
287    /// communicates with the hardware, whereas `kpal_plugin_new` is used merely to create the
288    /// plugin data structures.
289    pub plugin_init: unsafe extern "C" fn(*mut PluginData) -> c_int,
290
291    /// Returns an error message associated with a Plugin error code.
292    pub error_message_ns: extern "C" fn(c_int) -> *const c_uchar,
293
294    /// Returns the number of attributes of the plugin.
295    pub attribute_count:
296        unsafe extern "C" fn(plugin_data: *const PluginData, count: *mut size_t) -> c_int,
297
298    /// Returns the attribute IDs in a buffer provided by the caller.
299    pub attribute_ids:
300        unsafe extern "C" fn(plugin_data: *const PluginData, ids: *mut size_t, size_t) -> c_int,
301
302    /// Writes the name of an attribute to a buffer that is provided by the caller.
303    pub attribute_name: unsafe extern "C" fn(
304        plugin_data: *const PluginData,
305        id: size_t,
306        buffer: *mut c_uchar,
307        length: size_t,
308    ) -> c_int,
309
310    /// Indicates whether an attribute may be set before initialization.
311    pub attribute_pre_init: unsafe extern "C" fn(
312        plugin_data: *const PluginData,
313        id: size_t,
314        pre_init: *mut c_char,
315    ) -> c_int,
316
317    /// Writes the value of an attribute to a Value instance that is provided by the caller.
318    pub attribute_value: unsafe extern "C" fn(
319        plugin_data: *const PluginData,
320        id: size_t,
321        value: *mut Val,
322        phase: Phase,
323    ) -> c_int,
324
325    /// Sets the value of an attribute.
326    pub set_attribute_value: unsafe extern "C" fn(
327        plugin_data: *mut PluginData,
328        id: size_t,
329        value: *const Val,
330        phase: Phase,
331    ) -> c_int,
332}
333
334/// The type signature of the function that returns a new plugin instance.
335pub type KpalPluginInit = unsafe extern "C" fn(*mut Plugin) -> c_int;
336
337/// The type signature of the function that initializes a library.
338pub type KpalLibraryInit = unsafe extern "C" fn() -> c_int;
339
340/// The type signature of the collection of attributes that is owned by the plugin.
341pub type Attributes<T, E> = RefCell<MultiMap<usize, &'static str, Attribute<T, E>>>;
342
343/// A single piece of information that partly determines the state of a plugin.
344#[derive(Debug)]
345#[repr(C)]
346pub struct Attribute<T, E: Error + PluginError> {
347    /// The name of the attribute.
348    pub name: CString,
349
350    /// The value of the attribute.
351    ///
352    /// This field may be used to cache values retrieved from the hardware. This is the initial
353    /// value of non-constant attributes.
354    pub value: Value,
355
356    /// The callback functions that are fired when the attribute is either read or set during the
357    /// init phase of the plugin.
358    pub callbacks_init: Callbacks<T, E>,
359
360    /// The callback functions that are fired when the attribute is either read or set during the
361    /// run phase of the plugin.
362    pub callbacks_run: Callbacks<T, E>,
363}
364
365/// An owned value of an attribute.
366///
367/// Unlike the `Val` enum, these are intended to be owned by an instance of a PluginData struct and
368/// do not pass through the FFI.
369#[derive(Clone, Debug, PartialEq)]
370#[repr(C)]
371pub enum Value {
372    Int(c_int),
373    Double(c_double),
374    String(CString),
375    Uint(c_uint),
376}
377
378impl Value {
379    /// Returns a reference type to a Value.
380    ///
381    /// as_val creates a new Val instance from a Value. Value variants that contain datatypes that
382    /// implement Copy are copied into the new Val instance. For complex datatypes that are not
383    /// Copy, pointers to the data are embedded inside the Val instance instead.
384    ///
385    /// This method is used to generate datatypes that represent attribute values and that may pass
386    /// through the FFI.
387    pub fn as_val(&self) -> Val {
388        match self {
389            Value::Int(value) => Val::Int(*value),
390            Value::Double(value) => Val::Double(*value),
391            Value::String(value) => {
392                let slice = value.as_bytes_with_nul();
393                Val::String(slice.as_ptr(), slice.len())
394            }
395            Value::Uint(value) => Val::Uint(*value),
396        }
397    }
398}
399
400/// A wrapper type for transporting Values through the plugin API.
401///
402/// Unlike the `Value` enum, this type is intended to be sent through the FFI. Because of this, the
403/// enum variants can only contain C-compatible datatypes.
404#[derive(Clone, Debug, PartialEq)]
405#[repr(C)]
406pub enum Val {
407    Int(c_int),
408    Double(c_double),
409    String(*const c_uchar, size_t),
410    Uint(c_uint),
411}
412
413impl Val {
414    /// Clones the data inside a Val into a new Value type.
415    ///
416    /// This method is used to convert Vals, which pass through the FFI, into owned Value
417    /// datatypes. Wrapped data that is not Copy is necessarily cloned when the new Value instance
418    /// is created.
419    pub fn to_value(&self) -> Result<Value, ValueConversionError> {
420        match self {
421            Val::Int(value) => Ok(Value::Int(*value)),
422            Val::Double(value) => Ok(Value::Double(*value)),
423            Val::String(p_value, length) => {
424                let slice = unsafe { slice::from_raw_parts(*p_value, *length) };
425                let c_string = CStr::from_bytes_with_nul(slice)?.to_owned();
426                Ok(Value::String(c_string))
427            }
428            Val::Uint(value) => Ok(Value::Uint(*value)),
429        }
430    }
431}
432
433/// Callback functions that communicate with the hardware when an attribute is read or set.
434///
435/// The purpose of a callback is two-fold: it performs the actual communication with the hardware
436/// and/or it modifies the plugin's cached attribute data.
437///
438/// If the attribute is constant and never changes its original value, then the `Constant` variant
439/// should be used. If the attribute's value changes without user input (e.g. a sensor reading) but
440/// cannot be set, then use the `Get` variant. Otherwise, for attributes that can be both read and
441/// set, use the `GetAndSet` variant.
442///
443/// The Update variant is used to set only the cached value of attributes. Attributes that are
444/// Update always return their cached value when the attribute's value is read.
445#[repr(C)]
446pub enum Callbacks<T, E: Error + PluginError> {
447    Constant,
448    Get(fn(plugin: &T, cached: &Value) -> Result<Value, E>),
449    GetAndSet(
450        fn(plugin: &T, cached: &Value) -> Result<Value, E>,
451        fn(plugin: &T, cached: &Value, value: &Val) -> Result<(), E>,
452    ),
453    Update,
454}
455
456impl<T, E: Error + PluginError> fmt::Debug for Callbacks<T, E> {
457    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
458        use Callbacks::*;
459        match *self {
460            Constant => write!(f, "Constant"),
461            Get(get) => write!(f, "Get Callback: {:x}", get as usize),
462            GetAndSet(get, set) => write!(
463                f,
464                "Get Callback: {:x}, Set Callback: {:x}",
465                get as usize, set as usize
466            ),
467            Update => write!(f, "Update"),
468        }
469    }
470}
471
472/// Creates the required symbols for a plugin library.
473///
474/// Any plugin library must call this macro exactly once to generate the symbols that are required
475/// by the daemon.
476#[macro_export]
477macro_rules! declare_plugin {
478    ($plugin_type:ty, $plugin_err_type:ty) => {
479        /// Initializes the library.                                                                                                                                                                                      
480        ///                                                                                                                                                                                                               
481        /// This function is called only once by the daemon. It is called when a library is first
482        /// loaded into memory.
483        #[no_mangle]
484        pub extern "C" fn kpal_library_init() -> c_int {
485            env_logger::init();
486            PLUGIN_OK
487        }
488
489        /// Returns a new Plugin instance containing the plugin data and the function vtable.
490        ///
491        /// The plugin is used by the daemon to communicate with it. It contains an opaque pointer
492        /// to the plugin data and a vtable. The vtable is a struct of function pointers to the
493        /// methods in the plugin API.
494        ///
495        /// # Safety
496        ///
497        /// This function is unsafe because it dereferences a null pointer and assigns data to a
498        /// variable of the type `MaybeUnit`.
499        #[no_mangle]
500        pub unsafe extern "C" fn kpal_plugin_new(plugin: *mut Plugin) -> c_int {
501            let plugin_data = match <$plugin_type>::new() {
502                Ok(plugin_data) => plugin_data,
503                Err(e) => {
504                    log::error!("Failed to initialize the plugin: {:?}", e);
505                    return PLUGIN_INIT_ERR;
506                }
507            };
508
509            let plugin_data: Box<$plugin_type> = Box::new(plugin_data);
510            let plugin_data = Box::into_raw(plugin_data) as *mut PluginData;
511
512            let vtable = VTable {
513                plugin_free,
514                plugin_init: plugin_init::<$plugin_type, $plugin_err_type>,
515                error_message_ns,
516                attribute_count: attribute_count::<$plugin_type, $plugin_err_type>,
517                attribute_ids: attribute_ids::<$plugin_type, $plugin_err_type>,
518                attribute_name: attribute_name::<$plugin_type, $plugin_err_type>,
519                attribute_pre_init: attribute_pre_init::<$plugin_type, $plugin_err_type>,
520                attribute_value: attribute_value::<$plugin_type, $plugin_err_type>,
521                set_attribute_value: set_attribute_value::<$plugin_type, $plugin_err_type>,
522            };
523
524            plugin.write(Plugin {
525                plugin_data,
526                vtable,
527            });
528
529            log::debug!("Created new plugin: {:?}", plugin);
530            PLUGIN_OK
531        }
532    };
533}
534
535/// An error type that represents a failure to convert a Val to a Value.
536#[derive(Debug)]
537pub struct ValueConversionError {
538    side: FromBytesWithNulError,
539}
540
541impl Error for ValueConversionError {
542    fn source(&self) -> Option<&(dyn Error + 'static)> {
543        Some(&self.side)
544    }
545}
546
547impl fmt::Display for ValueConversionError {
548    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
549        write!(f, "PluginError: {:?}", self)
550    }
551}
552
553impl From<FromBytesWithNulError> for ValueConversionError {
554    fn from(error: FromBytesWithNulError) -> Self {
555        ValueConversionError { side: error }
556    }
557}