Expand description
§scuffle
scuffle provides an illumos-only wrapper around libscf,
focused on interactions with property groups and properties of existing
services, instances, and snapshots. Understanding SMF Properties is
highly recommended background reading.
There are large swathes of libscf that are currently not available via
scuffle due to its focus on properties, including:
- The ability to create or delete services
- Interacting with running instances or services in any way other than refreshing instances
- All functionality related to
snaplevels - All functionality related to templates
§Examples
Reading all properties from a specific property group in a service
instance’s running snapshot:
fn read_from_snapshot(
service_name: &str,
instance_name: &str,
property_group_name: &str,
) -> anyhow::Result<BTreeMap<String, Vec<Value>>> {
// Get a handle to scf and the local scope.
let scf = Scf::connect_current_zone()?;
let scope = scf.scope_local()?;
// Look up the property group within our snapshot by stepping through
// each level.
let Some(service) = scope.service(service_name)? else {
bail!("service {service_name} not found");
};
let Some(instance) = service.instance(instance_name)? else {
bail!("instance {instance_name} not found within {}", service.fmri());
};
let Some(snapshot) = instance.snapshot("running")? else {
bail!("no running snapshot found for {}", instance.fmri());
};
let Some(pg) = snapshot.property_group_composed(property_group_name)? else {
bail!(
"property group {property_group_name} not found for {}",
instance.fmri(),
);
};
let mut all_properties = BTreeMap::new();
for property in pg.properties()? {
let property = property?;
let values = property.values()?.collect::<Result<_, _>>()?;
all_properties.insert(property.name().to_string(), values);
}
Ok(all_properties)
}Adding a new property to an existing property group of a service instance:
fn add_new_property(
service_name: &str,
instance_name: &str,
property_group_name: &str,
property_name: &str,
value: ValueRef<'_>,
) -> anyhow::Result<()> {
// Get a handle to scf and the local scope.
let scf = Scf::connect_current_zone()?;
let scope = scf.scope_local()?;
// Look up the property group within our instance by stepping through
// each level.
let Some(service) = scope.service(service_name)? else {
bail!("service {service_name} not found");
};
let Some(instance) = service.instance(instance_name)? else {
bail!("instance {instance_name} not found within {}", service.fmri());
};
let Some(mut pg) = instance.property_group_direct(property_group_name)? else {
bail!(
"property group {property_group_name} not found for {}",
instance.fmri(),
);
};
// Open a transaction on the property group.
let tx = pg.transaction()?;
// Start the transaction. This takes a snapshot of the property group's
// current version; if it's modified by someone else between this point
// and our attempt to `commit()` below, we'll get an `OutOfDate` result.
let mut tx = tx.start()?;
// Add an entry to the transaction to create the new property.
tx.property_new(property_name, value)?;
// Commit the transaction.
match tx.commit()? {
TransactionCommitResult::Success(_committed_tx) => Ok(()),
TransactionCommitResult::OutOfDate(_reset_tx) => {
// We'll return an error for this example, but real code may
// want to call `pg.update()` and try again.
bail!("property group concurrently modified");
}
}
}See the examples/ directory for more complete examples.
§Error types
scuffle’s errors aim to provide extensive context; e.g., an error that
occurs while operating on an instance will include that instance’s FMRI.
These error types make extensive use of source errors as discussed in
Defining Error Types and Logging Errors. It is critical that
printing or logging of these error types walk the entire error chain as
discussed in that document, or the underlying error(s) will not be emitted.
The examples above use anyhow to allow easy ?-propagation despite the
multiple errors involved. scuffle does not currently provide a catch-all
error type of its own.
§Features
scuffle has three optional Cargo features:
- Enabling the
daftfeature adds implementations ofdaft::DiffabletoValue,ValueRef, andValueKind. - Enabling the
smf-by-instancefeature adds severalInstance::smf_*methods for controlling the SMF state of an instance, but requires alibscfthat includes recently-stabilized APIs. - Enabling the
testingfeature adds types to support writing tests that interact with SMF without needing to modify system-level SMF services / instances / properties; see “Testing Support” below.
§Stability
scuffle makes use of some non-public interfaces. Specifically:
Scf::connect_zone()uses an undocumented SCF handle decoration to connect tosvc.configdinside the specified zone. This matches howsvcadmandsvcpropimplement their-z zoneflags.- If the
smf-by-instanceCargo feature is not enabled,Instance::smf_refresh()uses a non-public function defined bylibscf_priv.h(_smf_refresh_instance_i()). - If the
testingfeature is enabled, it uses several other non-public interfaces; see “Testing Support” below.
§Testing Support
If the testing feature is enabled, scuffle exports an
isolated::IsolatedConfigd type that can run an instance of svc.configd
inside a temporary directory. After creating an instance of this type, tests
can connect to it via Scf::connect_isolated(), and then freely read and
write properties within that instance without touching the real system’s
svc.configd and without the permissions normally required to do that.
This comes with several caveats:
IsolatedConfigdtakes advantage of undocumented and uncommitted interfaces in at leastsvccfg,svc.configd, andlibscfitself. These may break in the future.- Refreshing instances inside an
IsolatedConfigdvialibscfdoes not work.scuffleworks around this by refreshing instances viasvccfgpointed at the isolatedsvc.configd, but this is a divergence from what production code will do forrefresh. - Non-persistent property groups do not work inside
IsolatedConfigd.
scuffle uses IsolatedConfigd for its own tests, and therefore does not
have test coverage on features that interact with these restrictions.
Modules§
- error
- Error types used throughout
scuffle. - isolated
- Module for running an isolated
svc.configdinstance.
Structs§
- Instance
- Handle to an SMF instance.
- Instances
- Iterator over all
Instances in aService. - Properties
- Iterator over all
Propertys in aPropertyGroup. - Property
- Handle to an SMF property within a property group.
- Property
Group - Handle to an SMF property group.
- Property
Groups - Iterator over all
PropertyGroups in an instance, service, or composed view (instance or snapshot). - Scf
- Entry-point handle to
libscf. - Scope
- Handle to an SMF scope.
- Service
- Handle to an SMF service.
- SmfMaintain
Flags - Optional flags for putting an SMF instance into maintenance.
- Snapshot
- Handle to an SMF snapshot.
- Snapshots
- Iterator over all
Snapshots in anInstance. - Transaction
- Transaction for modifying properties within a
PropertyGroup. - Value
Display Smf - Display adapter for
ValueandValueRef. - Values
- Iterator over all values in a
Property.
Enums§
- AddProperty
Group Flags - Flags controlling creation of new property groups.
- Delete
Property Group Result - Result of deleting a property group.
- Property
Group Composed - Type-state marker for a
PropertyGroupthat is from a composed view. - Property
Group Direct - Type-state marker for a
PropertyGroupthat is directly attached to anInstanceorService. - Property
Group Type - Property group types.
- Property
Group Update Result - Result of updating a property group against its latest version.
- SmfDegrade
Flags - Optional flags for putting an SMF instance into the degraded state.
- SmfEnable
Disable Flags - Optional flags for enabling or disabling SMF instances.
- Transaction
Commit Result - Result of committing a
Transaction. - Transaction
Committed - Type-state marker for a
Transactionin the committed state. - Transaction
Reset - Type-state marker for a
Transactionin the reset (initial) state. - Transaction
Started - Type-state marker for a
Transactionin the started state. - Value
- Owned representation of a
libscfvalue. - Value
Kind - Enum with a one-to-one mapping to
scf_type_t. - Value
Ref - Analogue of
Valuethat contains borrowed data.
Traits§
- Edit
Property Groups - Trait to add property groups to a
ServiceorInstance. - HasComposed
Property Groups - Trait to look up composed property groups from an
InstanceorSnapshot. - HasDirect
Property Groups - Trait to look up direct-attached property groups from a
ServiceorInstance.