kpal_plugin/
ffi.rs

1//! Functions and types used by the foreign function interface to communicate with a plugin.
2use std::boxed::Box;
3use std::convert::TryInto;
4use std::ptr::null;
5
6use libc::{c_char, c_int, c_uchar, size_t};
7
8use crate::error_codes::*;
9use crate::{
10    copy_string, PluginAPI, PluginData, PluginError, Val, ATTRIBUTE_PRE_INIT_FALSE,
11    ATTRIBUTE_PRE_INIT_TRUE, ERRORS,
12};
13
14/// Determines which callbacks to use by indicating the current lifecycle phase of the plugin when
15/// getting and setting attributes.
16pub type Phase = c_int;
17
18/// Frees the memory associated with the plugin's data.
19///
20/// This routine will be called automatically by the daemon and should not be called by any user
21/// code.
22///
23/// # Arguments
24///
25/// * `plugin_data` - A pointer to a PluginData struct
26pub extern "C" fn plugin_free(plugin_data: *mut PluginData) {
27    if plugin_data.is_null() {
28        return;
29    }
30    let plugin_data = plugin_data as *mut Box<PluginData>;
31    unsafe {
32        Box::from_raw(plugin_data);
33    }
34}
35
36/// Initializes a plugin.
37///
38/// # Safety
39///
40/// This function is unsafe because it dereferences a raw pointer.
41///
42/// # Arguments
43///
44/// * `plugin_data` - A pointer to a PluginData struct
45pub unsafe extern "C" fn plugin_init<T: PluginAPI<E>, E: PluginError + 'static>(
46    plugin_data: *mut PluginData,
47) -> c_int {
48    if plugin_data.is_null() {
49        log::error!("plugin_data pointer is null");
50        return NULL_PTR_ERR;
51    };
52
53    let plugin_data = plugin_data as *mut T;
54    match (*plugin_data).init() {
55        Ok(_) => {
56            log::debug!("Successfully initialized plugin");
57            PLUGIN_OK
58        }
59        Err(e) => {
60            log::error!("Plugin failed to initialize: {}", e);
61            e.error_code()
62        }
63    }
64}
65
66/// Returns an error message to the daemon given an error code.
67///
68/// If an undefined error code is provided, then this function will return a null pointer.
69pub extern "C" fn error_message_ns(error_code: c_int) -> *const c_uchar {
70    let error_code: size_t = match error_code.try_into() {
71        Ok(error_code) => error_code,
72        Err(_) => {
73            log::error!("Unrecognized error code provided");
74            return null();
75        }
76    };
77
78    ERRORS.get(error_code).map_or(null(), |e| e.as_ptr())
79}
80
81/// Returns the number of attributes of the plugin.
82///
83/// This function returns the number of attributes rather than a status code. If this function is
84/// provided with a null pointer as an argument, then zero will be returned.
85///
86/// # Safety
87///
88/// This function is unsafe because it dereferences a raw pointer.
89///
90/// # Arguments
91///
92/// * `plugin_data` - A pointer to a PluginData struct
93/// * `count` - A pointer to a size_t that will contain the number of attributes
94pub unsafe extern "C" fn attribute_count<T: PluginAPI<E>, E: PluginError + 'static>(
95    plugin_data: *const PluginData,
96    count: *mut size_t,
97) -> c_int {
98    if plugin_data.is_null() {
99        log::error!("plugin_data pointer is null");
100        return NULL_PTR_ERR;
101    };
102
103    let plugin_data = plugin_data as *const T;
104    *count = (*plugin_data).attribute_count();
105
106    PLUGIN_OK
107}
108
109/// Writes the plugin's attribute IDs to a buffer that is provided by the caller.
110///
111/// This function returns a status code that indicates whether the operation succeeded and the
112/// cause of any possible errors. It is recommended to check the status code returned by this
113/// function before reading the contents of the buffer.
114///
115/// # Safety
116///
117/// This function is unsafe because it dereferences a raw pointer.
118///
119/// # Arguments
120/// * `plugin_data` - A pointer to a PluginData struct
121/// * `buffer` - A pointer to a string of size_t's into which the attribute IDs will be written
122/// * `length` - The length of the buffer
123pub unsafe extern "C" fn attribute_ids<T: PluginAPI<E>, E: PluginError + 'static>(
124    plugin_data: *const PluginData,
125    buffer: *mut size_t,
126    length: size_t,
127) -> c_int {
128    if plugin_data.is_null() {
129        log::error!("plugin_data pointer is null");
130        return NULL_PTR_ERR;
131    }
132    let plugin_data = plugin_data as *const T;
133    let ids = (*plugin_data).attribute_ids();
134
135    match copy_string(&ids, buffer, length) {
136        Ok(_) => PLUGIN_OK,
137        Err(_) => UNDEFINED_ERR,
138    }
139}
140
141/// Writes the name of an attribute to a buffer that is provided by the caller.
142///
143/// This function returns a status code that indicates whether the operation succeeded and the
144/// cause of any possible errors.
145///
146/// # Safety
147///
148/// This function is unsafe because it dereferences a raw pointer.
149///
150/// # Arguments
151///
152/// * `plugin_data` - A pointer to a PluginData struct
153/// * `id` - The id of the attribute
154/// * `buffer` - A buffer of bytes into which the attribute's name will be written
155/// * `length` - The length of the buffer
156pub unsafe extern "C" fn attribute_name<T: PluginAPI<E>, E: PluginError + 'static>(
157    plugin_data: *const PluginData,
158    id: size_t,
159    buffer: *mut c_uchar,
160    length: size_t,
161) -> c_int {
162    if plugin_data.is_null() {
163        log::error!("plugin_data pointer is null");
164        return NULL_PTR_ERR;
165    }
166    let plugin_data = plugin_data as *const T;
167
168    match (*plugin_data).attribute_name(id) {
169        Ok(name) => copy_string(name.to_bytes_with_nul(), buffer, length)
170            .map(|_| PLUGIN_OK)
171            .unwrap_or_else(|_| UNDEFINED_ERR),
172        Err(e) => e.error_code(),
173    }
174}
175
176/// Indicates whether an attribute may be set before initialization.
177///
178/// This function accepts a pointer to a c_char. If the char is ATTRIBUTE_PRE_INIT_FALSE after the
179/// function returns and it returns a value of PLUGIN_OK, then the attribute that corresponds to
180/// the provided ID may not be set before plugin initialization. If the char is any value other
181/// than 0 and the function returns PLUGIN_OK, then the plugin may be set before initialization.
182///
183/// If the function does not return PLUGIN_OK, then the value stored at pre_init will not be
184/// modified.
185///
186/// # Safety
187///
188/// This function is unsafe because it dereferences raw pointers.
189///
190/// # Arguments
191///
192/// * `plugin_data` - A pointer to a PluginData struct
193/// * `id` - The id of the attribute
194/// * `pre_init` - A value that determines whether the attribute's value may be set before the
195/// plugin is initialized
196pub unsafe extern "C" fn attribute_pre_init<T: PluginAPI<E>, E: PluginError + 'static>(
197    plugin_data: *const PluginData,
198    id: size_t,
199    pre_init: *mut c_char,
200) -> c_int {
201    if plugin_data.is_null() {
202        log::error!("plugin_data pointer is null");
203        return NULL_PTR_ERR;
204    }
205    if pre_init.is_null() {
206        log::error!("pre_init pointer is null");
207        return NULL_PTR_ERR;
208    }
209    let plugin_data = plugin_data as *const T;
210
211    match (*plugin_data).attribute_pre_init(id) {
212        Ok(pre_init_resp) => {
213            log::debug!(
214                "Response for pre-init status of attribute {}: {}",
215                id,
216                pre_init_resp
217            );
218            if pre_init_resp {
219                *pre_init = ATTRIBUTE_PRE_INIT_TRUE;
220            } else {
221                *pre_init = ATTRIBUTE_PRE_INIT_FALSE;
222            };
223            PLUGIN_OK
224        }
225        Err(e) => e.error_code(),
226    }
227}
228
229/// Writes the value of an attribute to a Value instance that is provided by the caller.
230///
231/// This function returns a status code that indicates whether the operation succeeded and the
232/// cause of any possible errors.
233///
234/// # Safety
235///
236/// This function is unsafe because it dereferences a raw pointer.
237///
238/// # Arguments
239///
240/// * `plugin_data` - A pointer to a PluginData struct
241/// * `id` - The id of the attribute
242/// * `value` - A pointer to a Value enum. The enum is provided by this function's caller.
243/// * `phase` - The phase of the plugin lifecycle. This determines what callbacks to use to read
244/// the attribute value.
245pub unsafe extern "C" fn attribute_value<T: PluginAPI<E>, E: PluginError + 'static>(
246    plugin_data: *const PluginData,
247    id: size_t,
248    value: *mut Val,
249    phase: Phase,
250) -> c_int {
251    if plugin_data.is_null() {
252        log::error!("plugin_data pointer is null");
253        return NULL_PTR_ERR;
254    }
255    let plugin_data = plugin_data as *const T;
256
257    match (*plugin_data).attribute_value(id, phase) {
258        Ok(new_value) => {
259            log::debug!(
260                "Response for the value of attribute {}: {:?}",
261                id,
262                new_value
263            );
264            *value = new_value
265        }
266        Err(e) => return e.error_code(),
267    };
268
269    PLUGIN_OK
270}
271
272/// Sets the value of an attribute.
273///
274/// This function returns a status code that indicates whether the operation succeeded and the
275/// cause of any possible errors.
276///
277/// # Safety
278///
279/// This function is unsafe because it dereferences a raw pointer.
280///
281/// # Arguments
282///
283/// * `plugin_data` - A pointer to a PluginData struct
284/// * `id` - The id of the attribute
285/// * `value` - A pointer to a Val enum. The enum is provided by this function's caller and will be
286/// copied.
287/// * `phase` - The phase of the plugin lifecycle. This determines what callbacks to use to read
288/// the attribute value.
289pub unsafe extern "C" fn set_attribute_value<T: PluginAPI<E>, E: PluginError + 'static>(
290    plugin_data: *mut PluginData,
291    id: size_t,
292    value: *const Val,
293    phase: Phase,
294) -> c_int {
295    if plugin_data.is_null() {
296        log::error!("plugin_data pointer is null");
297        return NULL_PTR_ERR;
298    }
299    if value.is_null() {
300        log::error!("value pointer is null");
301        return NULL_PTR_ERR;
302    }
303    let plugin_data = plugin_data as *mut T;
304
305    match (*plugin_data).attribute_set_value(id, &*value, phase) {
306        Ok(_) => {
307            log::debug!("Set attribute {} to {:?}", id, *value);
308            PLUGIN_OK
309        }
310        Err(e) => e.error_code(),
311    }
312}