scuffle 0.1.0

High-level bindings to libscf on illumos
Documentation
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.

use crate::HasComposedPropertyGroups;
use crate::Instance;
use crate::PropertyGroup;
use crate::PropertyGroupComposed;
use crate::PropertyGroups;
use crate::Scf;
use crate::error::IterError;
use crate::error::LibscfError;
use crate::error::LookupError;
use crate::error::ScfEntity;
use crate::error::ScfEntityDescription;
use crate::error::ToEntityDescription;
use crate::iter::ScfIter;
use crate::scf::ScfObject;
use crate::utf8cstring::PropertyGroupFmri;
use crate::utf8cstring::Utf8CString;

/// Handle to an SMF snapshot.
///
/// Obtained by name via [`Instance::snapshot()`]. The most frequently-used
/// snapshot is the `"running"` snapshot, which provides a composed view of the
/// current effective configuration of an instance. This is the snapshot that is
/// updated when an instance is refreshed (e.g., via `svcadm refresh ...` or
/// [`Instance::smf_refresh()`]).
#[derive(Debug)]
pub struct Snapshot<'a> {
    instance: &'a Instance<'a>,
    name: Utf8CString,
    handle: ScfObject<'a, libscf_sys::scf_snapshot_t>,
}

impl<'a> Snapshot<'a> {
    pub(crate) fn new(
        instance: &'a Instance<'a>,
        name: &str,
    ) -> Result<Option<Self>, LookupError> {
        let name = Utf8CString::from_str(name).map_err(|err| {
            LookupError::InvalidName {
                entity: ScfEntity::Snapshot,
                name: name.to_string().into_boxed_str(),
                err,
            }
        })?;

        let mut handle = instance.scf().scf_snapshot_create()?;

        let result = unsafe {
            instance
                .scf_get_snapshot(name.as_c_str().as_ptr(), handle.as_mut_ptr())
        };

        match result {
            Ok(()) => Ok(Some(Self { instance, name, handle })),
            Err(LibscfError::NotFound) => Ok(None),
            Err(err) => Err(LookupError::Get {
                entity: ScfEntity::Snapshot,
                parent: instance.to_entity_description(),
                name: name.into_string().into_boxed_str(),
                err,
            }),
        }
    }

    pub(crate) fn scf(&self) -> &'a Scf<'a> {
        self.instance.scf()
    }

    pub(crate) unsafe fn scf_get_pg(
        &self,
        name: *const i8,
        pg: *mut libscf_sys::scf_propertygroup_t,
    ) -> Result<(), LibscfError> {
        unsafe {
            self.instance.scf_get_pg_composed(self.handle.as_ptr(), name, pg)
        }
    }

    /// The FMRI of this snapshot's parent [`Instance`].
    ///
    /// Snapshot names are not included in FMRI values.
    pub fn instance_fmri(&self) -> &str {
        self.instance.fmri()
    }

    pub(crate) fn property_group_fmri(
        &self,
        name: &Utf8CString,
    ) -> PropertyGroupFmri {
        self.instance.property_group_fmri(name)
    }

    /// The name of this snapshot.
    pub fn name(&self) -> &str {
        self.name.as_str()
    }
}

impl HasComposedPropertyGroups for Snapshot<'_> {
    fn property_group_composed(
        &self,
        name: &str,
    ) -> Result<Option<PropertyGroup<'_, PropertyGroupComposed>>, LookupError>
    {
        PropertyGroup::from_snapshot(self, name)
    }

    fn property_groups_composed(
        &self,
    ) -> Result<PropertyGroups<'_, PropertyGroupComposed>, IterError> {
        let iter = unsafe {
            self.instance.scf_iter_pgs_composed(self.handle.as_ptr())
        }?;
        Ok(PropertyGroups::from_snapshot(self, iter))
    }
}

impl ToEntityDescription for Snapshot<'_> {
    fn to_entity_description(&self) -> ScfEntityDescription {
        ScfEntityDescription::Snapshot {
            instance_fmri: self.instance_fmri().to_string().into_boxed_str(),
            name: self.name.to_string().into_boxed_str(),
        }
    }
}

/// Iterator over all [`Snapshot`]s in an [`Instance`].
///
/// Obtained via [`Instance::snapshots()`].
pub struct Snapshots<'a> {
    instance: &'a Instance<'a>,
    iter: ScfIter<'a, libscf_sys::scf_snapshot_t>,
}

impl<'a> Snapshots<'a> {
    pub(crate) fn new(
        instance: &'a Instance<'a>,
        iter: ScfIter<'a, libscf_sys::scf_snapshot_t>,
    ) -> Self {
        Self { instance, iter }
    }
}

impl<'a> Iterator for Snapshots<'a> {
    type Item = Result<Snapshot<'a>, IterError>;

    fn next(&mut self) -> Option<Self::Item> {
        self.iter
            .next_named(self.instance, || {
                self.instance.scf().scf_snapshot_create()
            })
            .map(|result| {
                result.map(|(name, handle)| Snapshot {
                    instance: self.instance,
                    name,
                    handle,
                })
            })
    }
}