1use std::ffi::c_void;
18use std::sync::Arc;
19
20use libloading::Library;
21use serde::de::DeserializeOwned;
22use serde::Serialize;
23
24use fidius_core::descriptor::{BufferStrategyKind, PluginDescriptor};
25use fidius_core::status::*;
26use fidius_core::wire;
27use fidius_core::PluginError;
28
29use crate::arena::{acquire_arena, grow_arena, release_arena, DEFAULT_ARENA_CAPACITY};
30use crate::error::{CallError, LoadError};
31use crate::types::PluginInfo;
32
33type FfiFn = unsafe extern "C" fn(*const u8, u32, *mut *mut u8, *mut u32) -> i32;
35
36type ArenaFn = unsafe extern "C" fn(*const u8, u32, *mut u8, u32, *mut u32, *mut u32) -> i32;
38
39pub struct PluginHandle {
48 _library: Option<Arc<Library>>,
52 vtable: *const c_void,
54 descriptor: *const PluginDescriptor,
58 free_buffer: Option<unsafe extern "C" fn(*mut u8, usize)>,
60 capabilities: u64,
62 method_count: u32,
64 info: PluginInfo,
66}
67
68unsafe impl Send for PluginHandle {}
77unsafe impl Sync for PluginHandle {}
78
79impl PluginHandle {
80 #[allow(dead_code)]
82 pub(crate) fn new(
83 library: Arc<Library>,
84 vtable: *const c_void,
85 descriptor: *const PluginDescriptor,
86 free_buffer: Option<unsafe extern "C" fn(*mut u8, usize)>,
87 capabilities: u64,
88 method_count: u32,
89 info: PluginInfo,
90 ) -> Self {
91 Self {
92 _library: Some(library),
93 vtable,
94 descriptor,
95 free_buffer,
96 capabilities,
97 method_count,
98 info,
99 }
100 }
101
102 pub fn from_loaded(plugin: crate::loader::LoadedPlugin) -> Self {
104 Self {
105 _library: Some(plugin.library),
106 vtable: plugin.vtable,
107 descriptor: plugin.descriptor,
108 free_buffer: plugin.free_buffer,
109 capabilities: plugin.info.capabilities,
110 method_count: plugin.method_count,
111 info: plugin.info,
112 }
113 }
114
115 pub fn from_descriptor(desc: &'static PluginDescriptor) -> Result<Self, LoadError> {
123 let info = PluginInfo {
124 name: unsafe { desc.plugin_name_str() }.to_string(),
125 interface_name: unsafe { desc.interface_name_str() }.to_string(),
126 interface_hash: desc.interface_hash,
127 interface_version: desc.interface_version,
128 capabilities: desc.capabilities,
129 buffer_strategy: desc
130 .buffer_strategy_kind()
131 .map_err(|v| LoadError::UnknownBufferStrategy { value: v })?,
132 runtime: crate::types::PluginRuntimeKind::Cdylib,
133 };
134 Ok(Self {
135 _library: None,
136 vtable: desc.vtable,
137 descriptor: desc as *const PluginDescriptor,
138 free_buffer: desc.free_buffer,
139 capabilities: desc.capabilities,
140 method_count: desc.method_count,
141 info,
142 })
143 }
144
145 pub fn find_in_process_descriptor(
152 plugin_name: &str,
153 ) -> Result<&'static PluginDescriptor, LoadError> {
154 let reg = fidius_core::registry::get_registry();
155 for i in 0..reg.plugin_count as usize {
156 let desc_ptr = unsafe { *reg.descriptors.add(i) };
157 let desc = unsafe { &*desc_ptr };
158 if unsafe { desc.plugin_name_str() } == plugin_name {
159 return Ok(desc);
160 }
161 }
162 Err(LoadError::PluginNotFound {
163 name: plugin_name.to_string(),
164 })
165 }
166
167 pub fn call_method<I: Serialize, O: DeserializeOwned>(
184 &self,
185 index: usize,
186 input: &I,
187 ) -> Result<O, CallError> {
188 if index >= self.method_count as usize {
190 return Err(CallError::InvalidMethodIndex {
191 index,
192 count: self.method_count,
193 });
194 }
195
196 let input_bytes =
197 wire::serialize(input).map_err(|e| CallError::Serialization(e.to_string()))?;
198
199 match self.info.buffer_strategy {
200 BufferStrategyKind::PluginAllocated => self.call_plugin_allocated(index, &input_bytes),
201 BufferStrategyKind::Arena => self.call_arena(index, &input_bytes),
202 }
203 }
204
205 pub fn call_method_raw(&self, index: usize, input: &[u8]) -> Result<Vec<u8>, CallError> {
215 if index >= self.method_count as usize {
216 return Err(CallError::InvalidMethodIndex {
217 index,
218 count: self.method_count,
219 });
220 }
221 match self.info.buffer_strategy {
222 BufferStrategyKind::PluginAllocated => self.call_plugin_allocated_raw(index, input),
223 BufferStrategyKind::Arena => self.call_arena_raw(index, input),
224 }
225 }
226
227 fn call_plugin_allocated<O: DeserializeOwned>(
230 &self,
231 index: usize,
232 input_bytes: &[u8],
233 ) -> Result<O, CallError> {
234 let fn_ptr = unsafe {
235 let fn_ptrs = self.vtable as *const FfiFn;
236 *fn_ptrs.add(index)
237 };
238
239 let mut out_ptr: *mut u8 = std::ptr::null_mut();
240 let mut out_len: u32 = 0;
241
242 let status = unsafe {
243 fn_ptr(
244 input_bytes.as_ptr(),
245 input_bytes.len() as u32,
246 &mut out_ptr,
247 &mut out_len,
248 )
249 };
250
251 match status {
252 STATUS_OK => {}
253 STATUS_BUFFER_TOO_SMALL => return Err(CallError::BufferTooSmall),
254 STATUS_SERIALIZATION_ERROR => {
255 return Err(CallError::Serialization("FFI serialization failed".into()))
256 }
257 STATUS_PLUGIN_ERROR => {
258 if !out_ptr.is_null() && out_len > 0 {
259 let output_slice =
260 unsafe { std::slice::from_raw_parts(out_ptr, out_len as usize) };
261 let plugin_err: PluginError = wire::deserialize(output_slice)
262 .map_err(|e| CallError::Deserialization(e.to_string()))?;
263
264 if let Some(free) = self.free_buffer {
265 unsafe { free(out_ptr, out_len as usize) };
266 }
267
268 return Err(CallError::Plugin(plugin_err));
269 }
270 return Err(CallError::Plugin(PluginError::new(
271 "UNKNOWN",
272 "plugin returned error but no error data",
273 )));
274 }
275 STATUS_PANIC => {
276 let msg = if !out_ptr.is_null() && out_len > 0 {
277 let slice = unsafe { std::slice::from_raw_parts(out_ptr, out_len as usize) };
278 let msg = wire::deserialize::<String>(slice)
279 .unwrap_or_else(|_| "unknown panic".into());
280 if let Some(free) = self.free_buffer {
281 unsafe { free(out_ptr, out_len as usize) };
282 }
283 msg
284 } else {
285 "unknown panic".into()
286 };
287 return Err(CallError::Panic(msg));
288 }
289 _ => return Err(CallError::UnknownStatus { code: status }),
290 }
291
292 if out_ptr.is_null() {
293 return Err(CallError::Serialization(
294 "plugin returned null output buffer".into(),
295 ));
296 }
297
298 let output_slice = unsafe { std::slice::from_raw_parts(out_ptr, out_len as usize) };
299 let result: Result<O, CallError> =
300 wire::deserialize(output_slice).map_err(|e| CallError::Deserialization(e.to_string()));
301
302 if let Some(free) = self.free_buffer {
303 unsafe { free(out_ptr, out_len as usize) };
304 }
305
306 result
307 }
308
309 fn call_arena<O: DeserializeOwned>(
314 &self,
315 index: usize,
316 input_bytes: &[u8],
317 ) -> Result<O, CallError> {
318 let fn_ptr = unsafe {
319 let fn_ptrs = self.vtable as *const ArenaFn;
320 *fn_ptrs.add(index)
321 };
322
323 let mut arena = acquire_arena(DEFAULT_ARENA_CAPACITY);
324 let mut out_offset: u32 = 0;
325 let mut out_len: u32 = 0;
326 let mut retried = false;
327
328 let status = loop {
329 let s = unsafe {
330 fn_ptr(
331 input_bytes.as_ptr(),
332 input_bytes.len() as u32,
333 arena.as_mut_ptr(),
334 arena.len() as u32,
335 &mut out_offset,
336 &mut out_len,
337 )
338 };
339 if s == STATUS_BUFFER_TOO_SMALL && !retried {
340 let needed = out_len as usize;
342 grow_arena(&mut arena, needed);
343 retried = true;
344 continue;
345 }
346 break s;
347 };
348
349 match status {
350 STATUS_OK => {
351 let start = out_offset as usize;
352 let end = start + out_len as usize;
353 if end > arena.len() {
354 release_arena(arena);
355 return Err(CallError::Serialization(
356 "plugin reported out_offset/out_len outside arena".into(),
357 ));
358 }
359 let result = wire::deserialize(&arena[start..end])
360 .map_err(|e| CallError::Deserialization(e.to_string()));
361 release_arena(arena);
362 result
363 }
364 STATUS_BUFFER_TOO_SMALL => {
365 release_arena(arena);
366 Err(CallError::BufferTooSmall)
367 }
368 STATUS_SERIALIZATION_ERROR => {
369 release_arena(arena);
370 Err(CallError::Serialization("FFI serialization failed".into()))
371 }
372 STATUS_PLUGIN_ERROR => {
373 let start = out_offset as usize;
374 let end = start + out_len as usize;
375 let plugin_err = if out_len > 0 && end <= arena.len() {
376 wire::deserialize::<PluginError>(&arena[start..end]).unwrap_or_else(|_| {
377 PluginError::new("UNKNOWN", "plugin returned malformed error")
378 })
379 } else {
380 PluginError::new("UNKNOWN", "plugin returned error but no error data")
381 };
382 release_arena(arena);
383 Err(CallError::Plugin(plugin_err))
384 }
385 STATUS_PANIC => {
386 release_arena(arena);
390 Err(CallError::Panic(
391 "plugin panicked (message not transmitted via Arena strategy)".into(),
392 ))
393 }
394 code => {
395 release_arena(arena);
396 Err(CallError::UnknownStatus { code })
397 }
398 }
399 }
400
401 fn call_plugin_allocated_raw(
405 &self,
406 index: usize,
407 input_bytes: &[u8],
408 ) -> Result<Vec<u8>, CallError> {
409 let fn_ptr = unsafe {
410 let fn_ptrs = self.vtable as *const FfiFn;
411 *fn_ptrs.add(index)
412 };
413
414 let mut out_ptr: *mut u8 = std::ptr::null_mut();
415 let mut out_len: u32 = 0;
416
417 let status = unsafe {
418 fn_ptr(
419 input_bytes.as_ptr(),
420 input_bytes.len() as u32,
421 &mut out_ptr,
422 &mut out_len,
423 )
424 };
425
426 match status {
427 STATUS_OK => {}
428 STATUS_BUFFER_TOO_SMALL => return Err(CallError::BufferTooSmall),
429 STATUS_SERIALIZATION_ERROR => {
430 return Err(CallError::Serialization("FFI serialization failed".into()))
431 }
432 STATUS_PLUGIN_ERROR => {
433 if !out_ptr.is_null() && out_len > 0 {
434 let output_slice =
435 unsafe { std::slice::from_raw_parts(out_ptr, out_len as usize) };
436 let plugin_err: PluginError = wire::deserialize(output_slice)
437 .map_err(|e| CallError::Deserialization(e.to_string()))?;
438 if let Some(free) = self.free_buffer {
439 unsafe { free(out_ptr, out_len as usize) };
440 }
441 return Err(CallError::Plugin(plugin_err));
442 }
443 return Err(CallError::Plugin(PluginError::new(
444 "UNKNOWN",
445 "plugin returned error but no error data",
446 )));
447 }
448 STATUS_PANIC => {
449 let msg = if !out_ptr.is_null() && out_len > 0 {
450 let slice = unsafe { std::slice::from_raw_parts(out_ptr, out_len as usize) };
451 let msg = wire::deserialize::<String>(slice)
452 .unwrap_or_else(|_| "unknown panic".into());
453 if let Some(free) = self.free_buffer {
454 unsafe { free(out_ptr, out_len as usize) };
455 }
456 msg
457 } else {
458 "unknown panic".into()
459 };
460 return Err(CallError::Panic(msg));
461 }
462 _ => return Err(CallError::UnknownStatus { code: status }),
463 }
464
465 if out_ptr.is_null() {
466 return Err(CallError::Serialization(
467 "plugin returned null output buffer".into(),
468 ));
469 }
470
471 let output_slice = unsafe { std::slice::from_raw_parts(out_ptr, out_len as usize) };
475 let result = output_slice.to_vec();
476
477 if let Some(free) = self.free_buffer {
478 unsafe { free(out_ptr, out_len as usize) };
479 }
480
481 Ok(result)
482 }
483
484 fn call_arena_raw(&self, index: usize, input_bytes: &[u8]) -> Result<Vec<u8>, CallError> {
487 let fn_ptr = unsafe {
488 let fn_ptrs = self.vtable as *const ArenaFn;
489 *fn_ptrs.add(index)
490 };
491
492 let mut arena = acquire_arena(DEFAULT_ARENA_CAPACITY);
493 let mut out_offset: u32 = 0;
494 let mut out_len: u32 = 0;
495 let mut retried = false;
496
497 let status = loop {
498 let s = unsafe {
499 fn_ptr(
500 input_bytes.as_ptr(),
501 input_bytes.len() as u32,
502 arena.as_mut_ptr(),
503 arena.len() as u32,
504 &mut out_offset,
505 &mut out_len,
506 )
507 };
508 if s == STATUS_BUFFER_TOO_SMALL && !retried {
509 let needed = out_len as usize;
510 grow_arena(&mut arena, needed);
511 retried = true;
512 continue;
513 }
514 break s;
515 };
516
517 match status {
518 STATUS_OK => {
519 let start = out_offset as usize;
520 let end = start + out_len as usize;
521 if end > arena.len() {
522 release_arena(arena);
523 return Err(CallError::Serialization(
524 "plugin reported out_offset/out_len outside arena".into(),
525 ));
526 }
527 let result = arena[start..end].to_vec();
528 release_arena(arena);
529 Ok(result)
530 }
531 STATUS_BUFFER_TOO_SMALL => {
532 release_arena(arena);
533 Err(CallError::BufferTooSmall)
534 }
535 STATUS_SERIALIZATION_ERROR => {
536 release_arena(arena);
537 Err(CallError::Serialization("FFI serialization failed".into()))
538 }
539 STATUS_PLUGIN_ERROR => {
540 let start = out_offset as usize;
541 let end = start + out_len as usize;
542 let plugin_err = if out_len > 0 && end <= arena.len() {
543 wire::deserialize::<PluginError>(&arena[start..end]).unwrap_or_else(|_| {
544 PluginError::new("UNKNOWN", "plugin returned malformed error")
545 })
546 } else {
547 PluginError::new("UNKNOWN", "plugin returned error but no error data")
548 };
549 release_arena(arena);
550 Err(CallError::Plugin(plugin_err))
551 }
552 STATUS_PANIC => {
553 release_arena(arena);
554 Err(CallError::Panic(
555 "plugin panicked (message not transmitted via Arena strategy)".into(),
556 ))
557 }
558 code => {
559 release_arena(arena);
560 Err(CallError::UnknownStatus { code })
561 }
562 }
563 }
564
565 pub fn has_capability(&self, bit: u32) -> bool {
569 if bit >= 64 {
570 return false;
571 }
572 self.capabilities & (1u64 << bit) != 0
573 }
574
575 pub fn info(&self) -> &PluginInfo {
577 &self.info
578 }
579
580 pub fn method_metadata(&self, method_id: u32) -> Vec<(&str, &str)> {
592 if method_id >= self.method_count {
593 return Vec::new();
594 }
595 let desc = unsafe { &*self.descriptor };
597 if desc.method_metadata.is_null() {
598 return Vec::new();
599 }
600 let entries =
603 unsafe { std::slice::from_raw_parts(desc.method_metadata, self.method_count as usize) };
604 let entry = &entries[method_id as usize];
605 if entry.kvs.is_null() || entry.kv_count == 0 {
606 return Vec::new();
607 }
608 let kvs = unsafe { std::slice::from_raw_parts(entry.kvs, entry.kv_count as usize) };
610 kvs.iter()
611 .map(|kv| {
612 let k = unsafe { std::ffi::CStr::from_ptr(kv.key) }
615 .to_str()
616 .expect("metadata key is not valid UTF-8");
617 let v = unsafe { std::ffi::CStr::from_ptr(kv.value) }
618 .to_str()
619 .expect("metadata value is not valid UTF-8");
620 (k, v)
621 })
622 .collect()
623 }
624
625 pub fn trait_metadata(&self) -> Vec<(&str, &str)> {
630 let desc = unsafe { &*self.descriptor };
632 if desc.trait_metadata.is_null() || desc.trait_metadata_count == 0 {
633 return Vec::new();
634 }
635 let kvs = unsafe {
637 std::slice::from_raw_parts(desc.trait_metadata, desc.trait_metadata_count as usize)
638 };
639 kvs.iter()
640 .map(|kv| {
641 let k = unsafe { std::ffi::CStr::from_ptr(kv.key) }
642 .to_str()
643 .expect("trait metadata key is not valid UTF-8");
644 let v = unsafe { std::ffi::CStr::from_ptr(kv.value) }
645 .to_str()
646 .expect("trait metadata value is not valid UTF-8");
647 (k, v)
648 })
649 .collect()
650 }
651}