# systemd-transient
Start transient systemd services using dbus, just like systemd-run.
## Usage
```rust,no_run
use systemd_transient::{ExecCommand, ServiceType, SystemdTransientManager, UnitExit, UnitProperty};
async fn example() -> Result<(), systemd_transient::Error> {
let manager = SystemdTransientManager::session().await?;
let unit = manager
.start(
"my-job", // becomes my-job.service
[
UnitProperty::Description("example job".into()),
UnitProperty::Type(ServiceType::Exec),
UnitProperty::ExecStart(vec![ExecCommand::new("/usr/bin/sleep", ["10"])]),
],
)
.await?;
match unit.wait_for_exit().await? {
UnitExit::Inactive => println!("exited cleanly"),
UnitExit::Failed => println!("failed"),
}
Ok(())
}
```
A name without a unit type suffix becomes a service. Other transient types
keep their suffix, e.g. `start("vm.slice", [])`. Start a transient slice before
any unit names it in `Slice=`: a slice that is only referenced is created
implicitly and is not transient. Use `escape_unit_name` for user-supplied
parts of a name; in slice names `-` means nesting.
`ExecCommand::new` uses the path as `argv[0]`; build an `ExecCommand` by hand
for a different `argv[0]` or `ignore_failure`.
`start` returns once the start job has finished, i.e. when systemd considers
the unit started; for `Type=oneshot` that is after the command has run.
Dropping or releasing the handle does not stop the unit.
`start` and `stop` have no client-side timeout. systemd applies the unit's
`TimeoutStartUSec`, `TimeoutStopUSec` and `JobTimeoutUSec` and fails the job
with `JobWaitFailed { reason: "timeout", .. }`. A `Type=oneshot` unit has no
start timeout unless you set one. If you wrap `start` in your own timeout and
drop the future, the job keeps running and you lose the handle.
`start` reuses the name of a unit that is loaded but failed or inactive, and
fails with `UnitAlreadyRunning` if the unit is running.
The handle starts the unit with `AddRef=true`, so systemd keeps it loaded after
it exits and `wait_for_exit` and `stop` can report its final state at any
time. Release the reference with `handle.release().await` or by dropping the
handle (released in a background task). Until then, `start` with the same
name fails with `UnitStillLoaded`. With `Restart=` and the default
`RestartMode=normal`, the unit passes through `failed` or `inactive` between
runs, so `wait_for_exit` returns after the first run.
`stop` only stops the run the handle started. If someone else started the
unit again (`systemctl start`), it returns `UnitRestarted` and leaves the new
run alone.
Properties not covered by `UnitProperty` can be passed as
`UnitProperty::Custom(name, value)`.
### Reusing a name
`ReusableTransientUnit` (from `manager.new_reusable(name, props)`) starts the
same unit again and again. `status()` reads the last run's state from the
exit watch without a D-Bus call, `release()` drops the reference without
stopping the unit, and `stop()` stops it and releases it.
### Taking over units
A handle's reference ends with its bus connection, so units outlive the
process that started them. `manager.adopt(name)`, or
`ReusableTransientUnit::adopt()`, takes over such a unit with a new reference
and reports its exit as `start` would. A failed unit stays loaded until it is
reset and can be adopted later; a unit that exited cleanly is collected once
nothing references it, and `adopt` then returns `None`.
`manager.list_units(&["prefix-*"])` lists loaded units by pattern, e.g. to
find units no handle claims, and `manager.stop_unit(name)` stops any unit by
its full name.
## License
Licensed under either of
* Apache License, Version 2.0
([LICENSE-APACHE](LICENSE-APACHE) or <http://www.apache.org/licenses/LICENSE-2.0>)
* MIT license
([LICENSE-MIT](LICENSE-MIT) or <http://opensource.org/licenses/MIT>)
at your option.
## Contribution
Unless you explicitly state otherwise, any contribution intentionally submitted
for inclusion in the work by you, as defined in the Apache-2.0 license, shall be
dual licensed as above, without any additional terms or conditions.