baryl 0.0.2

Public SDK for Baryl, a full-system emulation and introspection engine
//! The three things a component can ask of the run itself: rewind it, end it,
//! or checkpoint it.
//!
//! Reached as `ctl.core` from any event handler. None of the three answers
//! anything, and each is silently a no-op against a core that does not offer
//! it — there is no state you could have fixed, so there is nothing to check.

use core::ffi::CStr;

// Bindgen output cannot satisfy the workspace lints; the allow stops here.
mod generated {
    #![allow(non_camel_case_types, non_upper_case_globals, dead_code)]
    include!("generated.rs");
}
pub use generated::ComponentCoreRef;
#[doc(hidden)]
pub use generated::ComponentCoreVtable;

impl ComponentCoreRef {
    /// Ask for the guest to be rewound to the last checkpoint.
    ///
    /// `reason` rides along to whoever is watching resets — under a fuzz run
    /// that is the fuzz subsystem, which counts them by reason, and
    /// `BARYL_RESET_END`, `BARYL_RESET_TIMEOUT` and `BARYL_RESET_CRASH` are the
    /// three it knows. Any other value is yours to define.
    pub fn request_reset(&self, reason: u32) {
        // SAFETY: `vtable` is the core's static table, valid for the process.
        let Some(f) = (unsafe { (*self.vtable).request_reset }) else {
            return;
        };
        unsafe { f(*self, reason) };
    }

    /// Ask for the run to end, with a status and optionally a reason.
    ///
    /// `code` reaches whoever launched the run verbatim and unmasked — it is
    /// not truncated to a process status here, so a driver that wants one
    /// applies `& 0xff` itself. `message` is what a human reads afterwards, and
    /// must be `'static`: the pointer outlives this call, and a `c"..."`
    /// literal or a leaked string is what satisfies it.
    ///
    /// # Examples
    ///
    /// ```ignore
    /// #[core(first_ring_three)]
    /// fn done(&mut self, t: &mut Control) {
    ///     t.core.save_checkpoint(c"boot.ck");
    ///     t.core.request_exit(0, Some(c"took the checkpoint"));
    /// }
    /// ```
    pub fn request_exit(&self, code: i32, message: Option<&'static CStr>) {
        // SAFETY: as `request_reset`.
        let Some(f) = (unsafe { (*self.vtable).request_exit }) else {
            return;
        };
        let message = message.map_or(core::ptr::null(), |m| m.as_ptr());
        unsafe { f(*self, code, message) };
    }

    /// Queue a checkpoint of the whole machine to `path`.
    ///
    /// Queued, not immediate: the write happens at the top of the next exit
    /// from the guest, and the run carries on past it. Calling this does not
    /// end anything — pair it with [`request_exit`](Self::request_exit) if the
    /// checkpoint was the point of the run.
    ///
    /// `path` is read during the call and not retained.
    pub fn save_checkpoint(&self, path: &CStr) {
        // SAFETY: as `request_reset`.
        let Some(f) = (unsafe { (*self.vtable).save_checkpoint }) else {
            return;
        };
        unsafe { f(*self, path.as_ptr()) };
    }
}