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}