panda/plugins/
guest_plugin_manager.rs

1//! Bindings for the guest plugin manager
2//!
3//! The guest plugin manager is a PANDA plugin which manages "guest plugins", or programs
4//! which are injected into the guest and can communicate back to the host.
5//!
6//! See [`load_guest_plugin`] and [`channel_recv`] for more info.
7use crate::plugin_import;
8
9use std::{
10    ffi::{CStr, CString},
11    os::raw::c_char,
12    path::PathBuf,
13};
14
15mod guest_plugin;
16pub use guest_plugin::{load_guest_plugin, Channel, ChannelCB, ChannelId, GuestPlugin};
17
18mod from_channel_msg;
19pub use from_channel_msg::FromChannelMessage;
20
21/// Allows declaring a callback for recieving messages from a channel
22///
23/// Support functions with the signature `fn(u32, Msg)` where `u32` is the ID of the
24/// channel being written to, while `Msg` is a type that implements [`FromChannelMessage`]
25/// (&str, &[u8], String, etc).
26///
27/// ## Example
28///
29/// ```
30/// use panda::plugins::guest_plugin_manager::{load_guest_plugin, channel_recv};
31///
32/// // Print every message and which channel it's sent to
33/// #[channel_recv]
34/// fn receive_message_callback(channel: u32, message: &str) {
35///     println!("[channel {}] {}", channel, message);
36/// }
37///
38/// // Alternatively, use `Option<T>`/`Result<T, String>` to opt-in to handling invalid unicode
39/// #[channel_recv]
40/// fn receive_message_callback(channel: u32, message: Option<&str>) {
41///     if let Some(msg) = message {
42///         println!("[channel {}] {}", channel, msg);
43///     }
44/// }
45///
46/// // Or just ask for raw bytes
47/// #[channel_recv]
48/// fn receive_message_callback(_: u32, message: &[u8]) {
49///     println!("Message length: {}", message.len());
50/// }
51///
52/// #[panda::init]
53/// fn init() {
54///     load_guest_plugin("my_guest_plugin", receive_message_callback);
55/// }
56/// ```
57#[doc(inline)]
58pub use panda_macros::channel_recv;
59
60plugin_import! {
61    /// A PANDA plugin which manages "guest plugins", programs which are injected into
62    /// the guest which can communicate with the host process via "channels".test
63    ///
64    /// Unless you need greater control, it is recommended to use `load_guest_plugin` rather
65    /// than using the `GUEST_PLUGIN_MANAGER` object directly.
66    static GUEST_PLUGIN_MANAGER: GuestPluginManager = extern "guest_plugin_manager" {
67        /// Add a guest plugin to the guest plugin manager, loading it into the guest
68        /// as soon as possible.
69        fn add_guest_plugin(plugin: GuestPlugin) -> ChannelId;
70
71        /// Write to a channel, buffering the message until the guest performs a read
72        /// to the channel.
73        fn channel_write(channel: ChannelId, out: *const u8, out_len: usize);
74
75        /// Get a channel given a name, typically the name of the guest plugin it is
76        /// associated with, as each guest plugin is allocated a "main" channel of the
77        /// same name.
78        fn get_channel_from_name(channel_name: *const c_char) -> ChannelId;
79
80        /// Create a new channel given a callback for handling writes, returns the ID
81        /// of the newly allocated channel.
82        fn allocate_channel(callback: ChannelCB) -> ChannelId;
83    };
84}
85
86/// Get the guest plugin's path from its name, returning `None` if the guest plugin
87/// could not be found.
88pub fn guest_plugin_path(name: &str) -> Option<PathBuf> {
89    extern "C" {
90        fn panda_guest_plugin_path(name: *const c_char) -> *mut c_char;
91    }
92
93    let name = CString::new(name).ok()?;
94    let path_result = unsafe { panda_guest_plugin_path(name.as_ptr()) };
95
96    if path_result.is_null() {
97        None
98    } else {
99        let path = unsafe { CStr::from_ptr(path_result) };
100        let path = path.to_str().ok().map(PathBuf::from);
101
102        unsafe {
103            panda::sys::free(path_result as _);
104        }
105
106        path
107    }
108}